#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include "CIpodIap2.h"
#include "CIpodLog.h"
#include "CIpodChip.h"
#include "CIpodConfig.h"
#include "CIpodVehicle.h"
#include "CIpodEvent.h"

//net
// net
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

#define IAP2_POWER_CURRENT              1000        // mA
#define DEVICE_LANGUAGE_CHINESE         "zh_CN"
#define IPOD_ITEM_DEFAULT_NAME_CHINESE  "未知"        // 空字串默认名称
#define IPOD_ITEM_DEFAULT_NAME_ENGLISH  "Unknown"   // 空字串默认名称
#define MEDIA_TOP_LIST_SIZE             9

const char *MEDIA_TOP_LIST_CHINESE[] = {
        "播放列表", "表演者", "专辑", "风格", "歌曲", "作曲者", "有声读物", "播客", "在线电台",
};

const char *MEDIA_TOP_LIST_ENGLISH[] = {
        "Playlists", "Artists", "Albums", "Genres", "Songs", "Composers", "Audiobooks", "Podcasts", "iTunes Radio",
};

const uint8 JPG_HEADER[] = { 0xFF, 0xD8 };
const uint8 PNG_HEADER[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
const uint8 BMP_HEADER[] = { 0x42, 0x4D };

extern char gPairingIdentity[ 128 ];

struct mypara
{
    CIpodIap2 *cIpodIap2;
    char *thread_name;
};

static int _getBluetoothAddress( char * outAddress )
{
    FILE *  fd;
    char    buffer[64];
    char *  offset;
    char *  macAddress;

//    fd = fopen( "/data/bluetooth/deviceinfo.ini", "r" );
    //bt_addr=12:34:56:78:9A:BC
    fd = fopen( "/data/data/com.zjinnova.zlink/files/deviceinfo.ini", "r" );
    if( fd == NULL )
    {
        CIpodLog::e( "open file deviceinfo.ini failed.\n");
        return -1;
    }
    while( fgets(buffer, 63, fd) != NULL )
    {
        offset = strstr( buffer, "bt_addr" );
        if( offset != NULL )
        {
            macAddress = strstr( offset, "=" );
            strcpy( outAddress, macAddress + 1 );
            outAddress[ 17 ] = '\0';
            fclose( fd );
            return 0;
        }
    }

	fclose( fd );
	return 0;
}

CIpodIap2::CIpodIap2() {
    m_SyncNumber = rand();
    m_AckNumber = 0;
    m_AuthenticationState = IAP2AuthenticationState::NONE;
    m_IdentificationState = IAP2IdentificationState::NONE;
    m_ReqSerialNumber = false;
    m_TouchEnabled = false;
    m_PrductPlanUIDEnabled = true;
    m_A2dpConnected = false;
    m_MediaHID = 0;
}

CIpodIap2::~CIpodIap2() {

}

int CIpodIap2::init(int connMode) {

    int ret = -1;
    initMessages(connMode);
    if ((ret = syncLink(connMode)) == 0) {
        while (1)
        {
            if(((connMode == ConnMode_USB_HOST || connMode == ConnMode_USB_DEVICE) && isDeviceExist()) ||
                (connMode == ConnMode_BLUETOOTH && CIpodVehicle::getbtSppConnStatus() == ipod_SppState_Connected)||
                (connMode == ConnMode_WiFi && CIpodVehicle::getWiFiConnStatus() == WIFI_CONNECTED))
            {
                if (m_AuthenticationState == IAP2AuthenticationState::SUCCEEDED
                        && m_IdentificationState == IAP2IdentificationState::ACCEPTED) {
                    CIpodLog::w("iAP2 Control Session AuthenticationSucceeded and IdentificationAccepted\n");
                    ret = 0;
                    break;
                }
                if (m_AuthenticationState == IAP2AuthenticationState::FAILED) {
                    CIpodLog::i("IpodIap2::init, authentication failed!\n");
                    ret = -1;
                    break;
                }
                if (m_IdentificationState == IAP2IdentificationState::REJECTED) {
                    CIpodLog::i("IpodIap2::init, identification rejected!\n");
                    ret = -1;
                    break;
                }
            }else{
                CIpodLog::i("IpodIap2::init, connect exit!\n");
                return -1;
            }
            usleep(100000);
        }
    }

    if(connMode == ConnMode_BLUETOOTH){
        return ret;
    }
    IPODIdentification *identification = CIpodConfig::getIdentification();
    if (ret == 0 && identification->messages.bluetooth == 1) {
        ret = bluetoothComponentInformation(CIpodVehicle::getBtStatus());
        if (ret == 0) {
            ret = startBluetoothConnectionUpdates();
        }
    }
    if (ret == 0 && identification->messages.hid == 1) {
        for (std::list<USBDeviceHIDComponent>::iterator iter = identification->usbDeviceHIDs.begin(); iter != identification->usbDeviceHIDs.end(); ++iter) {
            if (iter->function == IPODHIDFunction::MEDIA) {
                m_MediaHID = iter->id;
                ret = startHID(m_MediaHID, IAP2HID::MEDIA_VID, IAP2HID::VEHICLE_PID, mediaDescriptor, sizeof(mediaDescriptor));
            }
        }
    }
    if (ret == 0 && identification->messages.media == 1) {
        ret = initMedia();
    }
    if (ret == 0 && identification->messages.playing == 1) {
        ret = startNowPlayingUpdates();
    }
    if (ret == 0 && identification->messages.power == 1) {
        ret = startPowerUpdates();
        if (ret == 0) {
            ret = powerSourceUpdate(IAP2_POWER_CURRENT);
        }
    }
    if (ret == 0 && identification->messages.audio == 1) {
        ret = startUSBDeviceModeAudio();
    }
    if (ret == 0 && identification->messages.communications == 1) {
        ret = startCallStateUpdates();
    }
    if (ret == 0 && identification->routeGuidance.id > 0)
    {
        ret = startRouteGuidanceUpdate();
    }

    return ret;
}

int CIpodIap2::deinit() {
    CIpodLog::i("CIpodIap2::deinit start.\n");

    int ret = 0;
    IPODIdentification *identification = CIpodConfig::getIdentification();
    if (ret == 0 && identification->messages.bluetooth == 1) {
        ret = stopBluetoothConnectionUpdates();
    }
    if (ret == 0 && identification->messages.hid == 1) {
        for (std::list<USBDeviceHIDComponent>::iterator iter = identification->usbDeviceHIDs.begin(); iter != identification->usbDeviceHIDs.end(); ++iter) {
            ret = stopHID(iter->id);
        }
    }
    if (ret == 0 && identification->messages.playing == 1) {
        ret = stopNowPlayingUpdates();
    }
    if (ret == 0 && identification->messages.power == 1) {
        ret = stopPowerUpdates();
    }
    if (ret == 0 && identification->messages.audio == 1) {
        ret = stopUSBDeviceModeAudio();
    }
    if (ret == 0 && identification->messages.media == 1) {
        ret = deinitMedia();
    }
    if (ret == 0 && identification->messages.communications == 1) {
        ret = stopCallStateUpdates();
    }
    if (ret == 0 && identification->routeGuidance.id > 0) {
        ret = stopRouteGuidanceUpdate();
    }
    CIpodLog::i("CIpodIap2::deinit exit successfully.\n");

    return 0;
}

int CIpodIap2::packetProc(const uint8 *data, uint16 len, bool native)
{
	// CIpodLog::w("CIpodIap2::packetProc rfcommData length: %d\n", len);
    if (native) {
        CIpodEvent::sendEvent(IPOD_EVT_EA_DATA_RECEIVED, getHandle(), len, data);
    } else {
        IAP2_LINK_PACKET_IN linkPacketIn;
        memcpy(&linkPacketIn.bytes, data, len);
        uint16 packetLen = IAP2_BIG_ENDIAN_SWITCH(linkPacketIn.header.length.valueFlipped);
        if (linkPacketIn.header.sessionID == IAP2_SESSION_ID_ACK) {
            if (packetLen != IAP2_LINK_HEADER_LENGTH) {
                m_AckNumber = linkPacketIn.header.sequenceNumber;
                sendAckPacket();
                if (linkPacketIn.header.controlByte.value == 0xC0) {
                    syncLinkResult(linkPacketIn.payload);
                }
            }else{
                if (linkPacketIn.header.controlByte.value == 0x10) {
                    CIpodLog::i("CIpodIap2::gaosheng -RST-- controlByte == %2x\n", linkPacketIn.header.controlByte.value);
                    // CIpodEvent::sendEvent(IPOD_EVT_CONNECT_STATUS_CHANGED, getHandle(), 2);
                }
            }
        } else {
            m_AckNumber = linkPacketIn.header.sequenceNumber;
            sendAckPacket();
            if (linkPacketIn.header.sessionID == IAP2_SESSION_ID_CONTROL) {
                if (linkPacketIn.payload[4] == IAP2_ACCESSORY_AUTHENTICATION && linkPacketIn.payload[5] == IAP2_AA_RequestAuthenticationCertificate) {
                    return requestAuthenticationCertificate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_AUTHENTICATION && linkPacketIn.payload[5] == IAP2_AA_RequestAuthenticationChallengeResponse) {
                    return requestAuthenticationChallengeResponse(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_AUTHENTICATION && linkPacketIn.payload[5] == IAP2_AA_AuthenticationFailed) {
                    return authenticationFailed();
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_AUTHENTICATION && linkPacketIn.payload[5] == IAP2_AA_AuthenticationSucceeded) {
                    return authenticationSucceeded();
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_IDENTIFICATION && linkPacketIn.payload[5] == IAP2_AI_StartIdentification) {
                    return startIdentification();
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_IDENTIFICATION && linkPacketIn.payload[5] == IAP2_AI_IdentificationAccepted) {
                    return identificationAccepted();
                } else if (linkPacketIn.payload[4] == IAP2_ACCESSORY_IDENTIFICATION && linkPacketIn.payload[5] == IAP2_AI_IdentificationRejected) {
                    return identificationRejected(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_ASSISTIVE_TOUCH && linkPacketIn.payload[5] == IAP2_AT_AssistiveTouchInformation) {
                    return assistiveTouchInformation(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_BLUETOOTH_CONNECTION && linkPacketIn.payload[5] == IAP2_BC_BluetoothConnectionUpdate) {
                    return bluetoothConnectionUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_COMMUNICATIONS && linkPacketIn.payload[5] == IAP2_C_CallStateUpdate) {
                    return callStateUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_COMMUNICATIONS && linkPacketIn.payload[5] == IAP2_C_CommunicationsUpdate) {
                    return communicationsUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_COMMUNICATIONS && linkPacketIn.payload[5] == IAP2_C_ListUpdate) {
                    return listUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_AUTHENICATION && linkPacketIn.payload[5] == IAP2_DA_DeviceAuthenticationCertificate) {
                    return deviceAuthenticationCertificate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_AUTHENICATION && linkPacketIn.payload[5] == IAP2_DA_DeviceAuthenticationResponse) {
                    return deviceAuthenticationResponse(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_DeviceInformationUpdate) {
					return deviceInformationUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_DeviceLanguageUpdate) {
                    return deviceLanguageUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_DeviceTimeUpdate) {
                    return deviceTimeUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_DeviceUUIDUpdate) {
                    return deviceUUIDUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_WirelessCarPlayUpdate) {
                    return wirelessCarPlayUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_DEVICE_NOTIFICATIONS && linkPacketIn.payload[5] == IAP2_DN_DeviceTransportIdentifierNotification)
                {
                    return deviceTransportIdentifierNotification(linkPacketIn.payload);
                }
                else if (linkPacketIn.payload[4] == IAP2_CARPLAY_CONNECTION && linkPacketIn.payload[5] == IAP2_CPC_CarPlayAvailability) {
                    return carPlayAvailability(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_EXTERNAL_ACCESSORY_PROTOCOL && linkPacketIn.payload[5] == IAP2_EAP_StartExternalAccessoryProtocolSession) {
                    return startExternalAccessoryProtocol(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_EXTERNAL_ACCESSORY_PROTOCOL && linkPacketIn.payload[5] == IAP2_EAP_StopExternalAccessoryProtocolSession) {
                    return stopExternalAccessoryProtocol(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_HID && linkPacketIn.payload[5] == IAP2_HID_DeviceHIDReport) {
                    return deviceHIDReport(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_HID && linkPacketIn.payload[5] == IAP2_HID_StartNativeHID) {
                    return startNativeHID();
                } else if (linkPacketIn.payload[4] == IAP2_LOCATION && linkPacketIn.payload[5] == IAP2_L_StartLocationInformation) {
                    return startLocationInformation(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_LOCATION && linkPacketIn.payload[5] == IAP2_L_StopLocationInformation) {
                    return stopLocationInformation();
                } else if (linkPacketIn.payload[4] == IAP2_MEDIA_LIBRARY_ACCESS && linkPacketIn.payload[5] == IAP2_MLA_MediaLibraryInformation) {
                    return mediaLibraryInformation(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_MEDIA_LIBRARY_ACCESS && linkPacketIn.payload[5] == IAP2_MLA_MediaLibraryUpdate) {
                    return mediaLibraryUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_NOW_PLAYING && linkPacketIn.payload[5] == IAP2_NP_NowPlayingUpdate) {
                    return nowPlayingUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_OOBBT_PAIRING && linkPacketIn.payload[5] == IAP2_OOBBTP_StartBluetoothPairing) {
                    return startBluetoothPairing(linkPacketIn.payload);
                }else if (linkPacketIn.payload[4] == IAP2_OOBBT_PAIRING && linkPacketIn.payload[5] == IAP2_OOBBTP_StopBluetoothPairing) {
                    return stopBluetoothPairing(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_R2_OOBBT_PAIRING && linkPacketIn.payload[5] == IAP2_R2_OOBBTP_StartOOBBTPairing) {
                    return startOOBBTPairing(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_R2_OOBBT_PAIRING && linkPacketIn.payload[5] == IAP2_R2_OOBBTP_OOBBTPairingLinkKeyInformation)
                {
                    return oOBBTPairingLinkKeyInformation(linkPacketIn.payload);
                }
                else if (linkPacketIn.payload[4] == IAP2_R2_OOBBT_PAIRING && linkPacketIn.payload[5] == IAP2_R2_OOBBTP_StopOOBBTPairing) {
                    return stopOOBBTPairing(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_POWER && linkPacketIn.payload[5] == IAP2_P_PowerUpdate) {
                    return powerUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_ROUTE_GUIDANCE && linkPacketIn.payload[5] == IAP2_RG_RouteGuidanceUpdate) {
                    return routeGuidanceUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_ROUTE_GUIDANCE && linkPacketIn.payload[5] == IAP2_RG_RouteGuidanceManeuverInformation) {
                    return routeGuidanceManeuverInformation(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_USB_DEVICE_MODE_AUDIO && linkPacketIn.payload[5] == IAP2_USBDMA_USBDeviceModeAudioInformation) {
                    return usbDeviceModeAudioInformatin(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_VEHICLE_STATUS && linkPacketIn.payload[5] == IAP2_VS_StartVehicleStatusUpdates) {
                    return startVehicleStatusUpdates(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_VEHICLE_STATUS && linkPacketIn.payload[5] == IAP2_VS_StopVehicleStatusUpdates) {
                    return stopVehicleStatusUpdates();
                } else if (linkPacketIn.payload[4] == IAP2_VOICE_OVER && linkPacketIn.payload[5] == IAP2_VO_VoiceOverUpdate) {
                    return voiceOverUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_VOICE_OVER && linkPacketIn.payload[5] == IAP2_VO_VoiceOverCursorUpdate) {
                    return voiceOverCursorUpdate(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_WIFI_INFORMATION_SHARING && linkPacketIn.payload[5] == IAP2_WIFIIS_WiFiInformation) {
                    return wifiInformation(linkPacketIn.payload);
                } else if (linkPacketIn.payload[4] == IAP2_WIFI_INFORMATION_SHARING && linkPacketIn.payload[5] == IAP2_WIFIIS_RequestAccessoryWiFiConfigurationInformation) {
                    return requestAccessoryWiFiConfigurationInformation();
                }
            } else if (linkPacketIn.header.sessionID == IAP2_SESSION_ID_FILE_TRANSFER) {
                return parseFileTransfer(linkPacketIn.payload, packetLen - IAP2_LINK_HEADER_LENGTH - 1);
            } else if (linkPacketIn.header.sessionID == IAP2_SESSION_ID_EA) {
                return parseEAData(linkPacketIn.payload, packetLen - IAP2_LINK_HEADER_LENGTH - 1);
            }
        }
    }
    return 0;
}

uint16 CIpodIap2::createLinkPacket(IAP2_CONTROL_BYTE control, uint8 syncNumber, uint8 ackNumber, uint8 sessionID, const uint8 *payload, uint16 payloadLen, IAP2_LINK_PACKET_OUT *desbuf) {
    uint16 packetLen = 0;
    uint32 checksum = 0;
    assert(desbuf != NULL);

    if (payloadLen == 0) {
        packetLen = IAP2_LINK_HEADER_LENGTH + payloadLen;
    } else {
        packetLen = IAP2_LINK_HEADER_LENGTH + payloadLen + 1;
    }
    desbuf->header.startOfPacketMSB = IAP2_LINK_SOM_MSB;
    desbuf->header.startOfPacketLSB = IAP2_LINK_SOM_LSB;
    desbuf->header.length.bytes.MSB = HIGH_BYTE(packetLen);
    desbuf->header.length.bytes.LSB = LOW_BYTE(packetLen);
    desbuf->header.controlByte = control;
    desbuf->header.sequenceNumber = syncNumber;
    desbuf->header.ackNumber = ackNumber;
    desbuf->header.sessionID = sessionID;
    for (int i = 0; i < IAP2_LINK_HEADER_LENGTH - 1; i++) {
        checksum += ((uint8 *)&desbuf->header)[i];
    }
    desbuf->header.checksum = (~((char) checksum)) + 1;
    checksum = 0;

    memcpy(&desbuf->payload, payload, payloadLen);

    for (int i = 0; i < payloadLen; i++) {
        checksum += payload[i];
    }
    desbuf->payload[payloadLen] = (~((char) checksum)) + 1;
    return packetLen;
}

uint16 CIpodIap2::createPayloadPacket(uint8 messageMainID, uint8 messageSubID, const uint8 *payload, uint16 payloadLen, uint8 *desbuf) {
    uint16 packetLen = payloadLen + IAP2_CONTROL_SESSION_HEADER_LENGTH;
    desbuf[0] = IAP2_CONTROL_SESSION_SOM_MSB;
    desbuf[1] = IAP2_CONTROL_SESSION_SOM_LSB;
    desbuf[2] = HIGH_BYTE(packetLen);
    desbuf[3] = LOW_BYTE(packetLen);
    desbuf[4] = messageMainID;
    desbuf[5] = messageSubID;
    memcpy(desbuf + IAP2_CONTROL_SESSION_HEADER_LENGTH, payload, payloadLen);
    return packetLen;
}

uint16 CIpodIap2::createParamPacket(uint16 paramID, const uint8 *param, uint16 paramLen, uint8 *desbuf) {
    uint16 packetLen = paramLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    desbuf[0] = HIGH_BYTE(packetLen);
    desbuf[1] = LOW_BYTE(packetLen);
    desbuf[2] = HIGH_BYTE(paramID);
    desbuf[3] = LOW_BYTE(paramID);
    memcpy(desbuf + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, param, paramLen);
    return packetLen;
}

void CIpodIap2::increaseSyncNumber() {
    if (m_SyncNumber == 0xFF) {
        m_SyncNumber = 0;
    } else {
        ++m_SyncNumber;
    }
}

int CIpodIap2::write(const uint8 *data, uint16 len) {
    if(isExiting() || !isDeviceExist()){
        CIpodLog::i("CIpodIap2::write, ipod is destroyed, exiting...!\n");
        return -1;
    }

    return send(data, len, false);
}

int CIpodIap2::sendAckPacket() {
    IAP2_LINK_PACKET_OUT linkPacketOut;
    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    uint16 packetLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_ACK, NULL, 0, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetLen);
}

int CIpodIap2::syncLink(int connMode) {
    CIpodLog::i("[ Accessory ] step 3:  SYN\n");

    uint8 syncBuf[128] = { 0 };
    uint16 syncLen = 0, packetLen = 0;

    syncBuf[syncLen++] = IAP2_LINK_VERSION;
    syncBuf[syncLen++] = IAP2_MAXIMUM_NUMBER_OF_OUTSTANDING_PACKETS;
    syncBuf[syncLen++] = HIGH_BYTE(IAP2_MAXIMUM_RECEIVED_PACKET_LENGTH);
    syncBuf[syncLen++] = LOW_BYTE(IAP2_MAXIMUM_RECEIVED_PACKET_LENGTH);
    if( connMode == ConnMode_BLUETOOTH || connMode == ConnMode_WiFi )
    {
        syncBuf[syncLen++] = HIGH_BYTE(2820);
        syncBuf[syncLen++] = LOW_BYTE(2820);
        syncBuf[syncLen++] = HIGH_BYTE(5888);
        syncBuf[syncLen++] = LOW_BYTE(5888);
    }
    else
    {
        syncBuf[syncLen++] = HIGH_BYTE(IAP2_RETRANSMISSION_TIMEOUT);
        syncBuf[syncLen++] = LOW_BYTE(IAP2_RETRANSMISSION_TIMEOUT);
        syncBuf[syncLen++] = HIGH_BYTE(IAP2_CUMULATIVE_ACKNOWLEDGEMENT_TIMEOUT);
        syncBuf[syncLen++] = LOW_BYTE(IAP2_CUMULATIVE_ACKNOWLEDGEMENT_TIMEOUT);
    }
    syncBuf[syncLen++] = IAP2_MAXIMUM_NUMBER_OF_RETRANSMISSIONS;
    syncBuf[syncLen++] = IAP2_MAXIMUM_CUMULATIVE_ACKNOWLEDGEMENTS;
    syncBuf[syncLen++] = IAP2_SESSION_ID_CONTROL;
    syncBuf[syncLen++] = IAP2_SESSION_TYPE_CONTROL;
    if (CIpodConfig::getMode() == Mode_CARPLAY) {
        syncBuf[syncLen++] = IAP2_SESSION_VERSION_CONTROL_CARPLAY;
    } else {
        syncBuf[syncLen++] = IAP2_SESSION_VERSION_CONTROL_NORMAL;
    }

    if (CIpodConfig::getIdentification()->messages.media == 1
            || CIpodConfig::getIdentification()->messages.playing == 1) {
        syncBuf[syncLen++] = IAP2_SESSION_ID_FILE_TRANSFER;
        syncBuf[syncLen++] = IAP2_SESSION_TYPE_FILE_TRANSFER;
        syncBuf[syncLen++] = IAP2_SESSION_VERSION_FILE_TRANSFER;
    }

    if (CIpodConfig::getIdentification()->messages.eap == 1 && CIpodConfig::getIdentification()->eaprotocol.nativeid == 0) {
        syncBuf[syncLen++] = IAP2_SESSION_ID_EA;
        syncBuf[syncLen++] = IAP2_SESSION_TYPE_EA;
        syncBuf[syncLen++] = IAP2_SESSION_VERSION_EA;
    }

    IAP2_CONTROL_BYTE control;
    control.value = 0x80;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    packetLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_ACK, syncBuf, syncLen, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetLen);
}

int CIpodIap2::syncLinkResult(const uint8 *payload) {
    CIpodLog::i("[  Device   ] step 4:  SYN+ACK\n");
    return 0;
}

int CIpodIap2::initMessages(int connMode) {
    CIpodLog::i("Prepare MessagesSentByAccessory and MessagesReceivedFromDevice.\n");

    m_MessagesSent.clear();
    m_MessagesReceived.clear();

    IPODIdentification *identification = CIpodConfig::getIdentification();
    if (identification->messages.serialnumber == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AA_AccessoryAuthenticationSerialNumber, IAP2_ACCESSORY_AUTHENTICATION));
    }
    if (identification->messages.app == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AL_RequestAppLaunch, IAP2_APP_LAUNCH));
    }
    if (identification->messages.touch == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AT_StartAssistiveTouch, IAP2_ASSISTIVE_TOUCH));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AT_StopAssistiveTouch, IAP2_ASSISTIVE_TOUCH));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AT_StartAssistiveTouchInformation, IAP2_ASSISTIVE_TOUCH));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_AT_StopAssistiveTouchInformation, IAP2_ASSISTIVE_TOUCH));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_AT_AssistiveTouchInformation, IAP2_ASSISTIVE_TOUCH));
    }
    if (identification->messages.bluetooth == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_BC_BluetoothComponentInformation, IAP2_BLUETOOTH_CONNECTION));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_BC_StartBluetoothConnectionUpdates, IAP2_BLUETOOTH_CONNECTION));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_BC_StopBluetoothConnectionUpdates, IAP2_BLUETOOTH_CONNECTION));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_BC_BluetoothConnectionUpdate, IAP2_BLUETOOTH_CONNECTION));
    }
    if (identification->messages.communications == 1)
    {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StartCallStateUpdates, IAP2_COMMUNICATIONS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StopCallStateUpdates, IAP2_COMMUNICATIONS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_C_CallStateUpdate, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StartCommunicationsUpdates, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StopCommunicationsUpdates, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_InitiateCall, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_AcceptCall, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_EndCall, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_SwapCalls, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_MergeCalls, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_HoldStatusUpdate, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_MuteStatusUpdate, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_SendDTMF, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StartListUpdates, IAP2_COMMUNICATIONS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_C_StopListUpdates, IAP2_COMMUNICATIONS));
        // m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_C_CommunicationsUpdate, IAP2_COMMUNICATIONS));
        // m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_C_ListUpdate, IAP2_COMMUNICATIONS));
    }
    if (identification->messages.authentication == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_DA_RequestDeviceAuthenticationCertificate, IAP2_DEVICE_AUTHENICATION));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_DA_RequestDeviceAuthenticationChallengeResponse, IAP2_DEVICE_AUTHENICATION));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_DA_DeviceAuthenticationFailed, IAP2_DEVICE_AUTHENICATION));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_DA_DeviceAuthenticationSucceeded, IAP2_DEVICE_AUTHENICATION));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DA_DeviceAuthenticationCertificate, IAP2_DEVICE_AUTHENICATION));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DA_DeviceAuthenticationResponse, IAP2_DEVICE_AUTHENICATION));
    }
    if (identification->messages.notifications == 1)
    {
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_DeviceInformationUpdate, IAP2_DEVICE_NOTIFICATIONS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_DeviceLanguageUpdate, IAP2_DEVICE_NOTIFICATIONS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_DeviceTransportIdentifierNotification, IAP2_DEVICE_NOTIFICATIONS));
        // m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_DeviceTimeUpdate, IAP2_DEVICE_NOTIFICATIONS));
        // m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_DeviceUUIDUpdate, IAP2_DEVICE_NOTIFICATIONS));

        if( connMode == ConnMode_BLUETOOTH || connMode == ConnMode_USB_HOST )
        {
            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_DN_WirelessCarPlayUpdate, IAP2_DEVICE_NOTIFICATIONS));
//            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_CPC_CarPlayAvailability, IAP2_CARPLAY_CONNECTION));
//            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_CPC_CarPlayStartSession, IAP2_CARPLAY_CONNECTION));
        }
    }

    if (identification->messages.eap == 1 && identification->eaprotocol.nativeid == 0) {
//        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_EAP_StatusExternalAccessoryProtocolSession, IAP2_EXTERNAL_ACCESSORY_PROTOCOL));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_EAP_StartExternalAccessoryProtocolSession, IAP2_EXTERNAL_ACCESSORY_PROTOCOL));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_EAP_StopExternalAccessoryProtocolSession, IAP2_EXTERNAL_ACCESSORY_PROTOCOL));
    }

    if (identification->messages.hid == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_HID_StartHID, IAP2_HID));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_HID_AccessoryHIDReport, IAP2_HID));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_HID_StopHID, IAP2_HID));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_HID_DeviceHIDReport, IAP2_HID));
        //m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_HID_StartNativeHID, IAP2_HID));
    }
    if (identification->messages.location == 1)
    {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_L_LocationInformation, IAP2_LOCATION));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_L_StartLocationInformation, IAP2_LOCATION));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_L_StopLocationInformation, IAP2_LOCATION));
    }
    if (identification->messages.media == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_StartMediaLibraryInformation, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_StopMediaLibraryInformation, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_StartMediaLibraryUpdates, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_StopMediaLibraryUpdates, IAP2_MEDIA_LIBRARY_ACCESS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_PlayMediaLibraryCurrentSelection, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_PlayMediaLibraryItems, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_PlayMediaLibraryCollection, IAP2_MEDIA_LIBRARY_ACCESS));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_PlayMediaLibrarySpecial, IAP2_MEDIA_LIBRARY_ACCESS));

        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_MediaLibraryInformation, IAP2_MEDIA_LIBRARY_ACCESS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_MLA_MediaLibraryUpdate, IAP2_MEDIA_LIBRARY_ACCESS));
    }
    if (identification->messages.playing == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_NP_StartNowPlayingUpdates, IAP2_NOW_PLAYING));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_NP_StopNowPlayingUpdates, IAP2_NOW_PLAYING));
        // m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_NP_SetNowPlayingInformation, IAP2_NOW_PLAYING));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_NP_NowPlayingUpdate, IAP2_NOW_PLAYING));
    }
    if (identification->messages.power == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_P_StartPowerUpdates, IAP2_POWER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_P_StopPowerUpdates, IAP2_POWER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_P_PowerSourceUpdate, IAP2_POWER));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_P_PowerUpdate, IAP2_POWER));
    }
    if (identification->messages.audio == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_USBDMA_StartUSBDeviceModeAudio, IAP2_USB_DEVICE_MODE_AUDIO));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_USBDMA_StopUSBDeviceModeAudio, IAP2_USB_DEVICE_MODE_AUDIO));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_USBDMA_USBDeviceModeAudioInformation, IAP2_USB_DEVICE_MODE_AUDIO));
    }
    if (identification->messages.vehicle == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VS_VehicleStatusUpdate, IAP2_VEHICLE_STATUS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_VS_StartVehicleStatusUpdates, IAP2_VEHICLE_STATUS));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_VS_StopVehicleStatusUpdates, IAP2_VEHICLE_STATUS));
    }
    if (identification->messages.voiceover == 1) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StartVoiceOver, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StopVoiceOver, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverMoveCursor, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverActivateCursor, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverScrollPage, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverSpeakText, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverPauseText, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverResumeText, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StartVoiceOverUpdates, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StopVoiceOverUpdates, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_RequestVoiceOverConfiguration, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StartVoiceOverCursorUpdates, IAP2_VOICE_OVER));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_VO_StopVoiceOverCursorUpdates, IAP2_VOICE_OVER));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_VO_VoiceOverUpdate, IAP2_VOICE_OVER));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_VO_VoiceOverCursorUpdate, IAP2_VOICE_OVER));
    }
    if (identification->messages.wifi == 1) {
        if(connMode == ConnMode_BLUETOOTH)
        {
            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_WIFIIS_AccessoryWiFiConfigurationInformation, IAP2_WIFI_INFORMATION_SHARING));
            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_WIFIIS_RequestAccessoryWiFiConfigurationInformation, IAP2_WIFI_INFORMATION_SHARING));
        }
    }
    if (identification->messages.oob == 1) {
//        if(connMode == ConnMode_BLUETOOTH){
//            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_OOBBTP_StartBluetoothPairing, IAP2_OOBBT_PAIRING));
//            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_OOBBTP_BluetoothPairingAccessoryInformation, IAP2_OOBBT_PAIRING));
//            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_OOBBTP_BluetoothPairingStatus, IAP2_OOBBT_PAIRING));
//            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_OOBBTP_StopBluetoothPairing, IAP2_OOBBT_PAIRING));

            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_R2_OOBBTP_StartOOBBTPairing, IAP2_R2_OOBBT_PAIRING));
            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_R2_OOBBTP_OOBBTPairingAccessoryInformation, IAP2_R2_OOBBT_PAIRING));
            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_R2_OOBBTP_OOBBTPairingLinkKeyInformation, IAP2_R2_OOBBT_PAIRING));
            m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_R2_OOBBTP_OOBBTPairingCompletionInformation, IAP2_R2_OOBBT_PAIRING));
            m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_R2_OOBBTP_StopOOBBTPairing, IAP2_R2_OOBBT_PAIRING));
//        }
    }

    if (identification->routeGuidance.id > 0) {
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_RG_StartRouteGuidanceUpdates, IAP2_ROUTE_GUIDANCE));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_RG_RouteGuidanceUpdate, IAP2_ROUTE_GUIDANCE));
        m_MessagesReceived.push_back(IAP2_BIG_ENDIAN(IAP2_RG_RouteGuidanceManeuverInformation, IAP2_ROUTE_GUIDANCE));
        m_MessagesSent.push_back(IAP2_BIG_ENDIAN(IAP2_RG_StopRouteGuidanceUpdates, IAP2_ROUTE_GUIDANCE));
    }
    return 0;
}

int CIpodIap2::parseFileTransfer(const uint8 *data, uint16 len) {
    uint8 id = data[0];
    IAP2FileTransfer *transfer = NULL;
    for (std::list<IAP2FileTransfer *>::iterator iter = m_FileTransfers.begin(); iter != m_FileTransfers.end(); ++iter) {
        if ((*iter)->id == id) {
            transfer = *iter;
            break;
        }
    }
    if (transfer == NULL) {
        CIpodLog::i("CIpodIap2::parseFileTransfer, transfer id has not received!\n");
        return -1;
    }

    uint16 paramLen = 0;
    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    switch (data[1]) {
    case IAP2_FILE_TRANSFER_SETUP_DATAGRAM: {
        uint64 tempSize = 0;
        for (uint32 i = 0; i < 8; i++) {
            tempSize = tempSize * 256 + data[i + IAP2_FILE_PAYLOAD_HEADER_LENGTH] ;
        }
        transfer->total = tempSize;
        transfer->received = 0;
        if (transfer->total > 0) {
            transfer->data = (uint8 *) malloc(transfer->total);
            uint8 buf[2] = { data[0], IAP2_FILE_TRANSFER_START };
            increaseSyncNumber();
            paramLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_FILE_TRANSFER, buf, 2, &linkPacketOut);
            write((uint8 *)&linkPacketOut, paramLen);
        } else {
            uint8 buf[2] = { data[0], IAP2_FILE_TRANSFER_SUCCESS };
            increaseSyncNumber();
            paramLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_FILE_TRANSFER, buf, 2, &linkPacketOut);
            write((uint8 *)&linkPacketOut, paramLen);
            saveFileTransfer(transfer);
        }
        break;
    }
    case IAP2_FILE_TRANSFER_FIRST_DATA: {
        memcpy(transfer->data + transfer->received, data + IAP2_FILE_PAYLOAD_HEADER_LENGTH, len - IAP2_FILE_PAYLOAD_HEADER_LENGTH);
        transfer->received = len - IAP2_FILE_PAYLOAD_HEADER_LENGTH;
        break;
    }
    case IAP2_FILE_TRANSFER_FIRST_AND_ONLY_DATA: {
        memcpy(transfer->data + transfer->received, data + IAP2_FILE_PAYLOAD_HEADER_LENGTH, len - IAP2_FILE_PAYLOAD_HEADER_LENGTH);
        transfer->received = len - IAP2_FILE_PAYLOAD_HEADER_LENGTH;
        increaseSyncNumber();
        uint8 buf[2] = { data[0], IAP2_FILE_TRANSFER_SUCCESS };
        paramLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_FILE_TRANSFER, buf, 2, &linkPacketOut);
        write((uint8 *)&linkPacketOut, paramLen);
        saveFileTransfer(transfer);
        break;
    }
    case IAP2_FILE_TRANSFER_DATA: {
        memcpy(transfer->data + transfer->received, data + IAP2_FILE_PAYLOAD_HEADER_LENGTH, len - IAP2_FILE_PAYLOAD_HEADER_LENGTH);
        transfer->received = transfer->received + len - IAP2_FILE_PAYLOAD_HEADER_LENGTH;
        break;
    }
    case IAP2_FILE_TRANSFER_LAST_DATA: {
        memcpy(transfer->data + transfer->received, data + IAP2_FILE_PAYLOAD_HEADER_LENGTH, len - IAP2_FILE_PAYLOAD_HEADER_LENGTH);
        transfer->received = transfer->received + len - IAP2_FILE_PAYLOAD_HEADER_LENGTH;
        unsigned char buf[2] = { data[0], IAP2_FILE_TRANSFER_SUCCESS};
        increaseSyncNumber();
        paramLen = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_FILE_TRANSFER, buf, 2, &linkPacketOut);
        write((uint8 *)&linkPacketOut, paramLen);
        saveFileTransfer(transfer);
        break;
    }
    case IAP2_FILE_TRANSFER_START:
        break;
    case IAP2_FILE_TRANSFER_CANCEL:
        break;
    case IAP2_FILE_TRANSFER_PAUSE:
        break;
    case IAP2_FILE_TRANSFER_SUCCESS:
        break;
    case IAP2_FILE_TRANSFER_FAILURE:
        break;
    }
    return 0;
}

int CIpodIap2::saveFileTransfer(IAP2FileTransfer *transfer) {
    switch (transfer->type) {
    case IAP2FileTransferType::ARTWORK: {
        char buf[128] = { 0 };
        char path[128] = { 0 };
        const char *cachePath = CIpodVehicle::getCachePath();
        sprintf(buf, "rm -rf %s/Pictures%s/album_artwork.*", cachePath, m_NowPlaying.libraryID.c_str());
        system(buf);
        if (transfer->total > 0) {
            if (memcmp(transfer->data, JPG_HEADER, sizeof(JPG_HEADER)) == 0) {
                sprintf(path, "%s/Pictures%s/album_artwork.jpg", cachePath, m_NowPlaying.libraryID.c_str());
            } else if (memcmp(transfer->data, PNG_HEADER, sizeof(PNG_HEADER)) == 0) {
                sprintf(path, "%s/Pictures%s/album_artwork.png", cachePath, m_NowPlaying.libraryID.c_str());
            } else if (memcmp(transfer->data, BMP_HEADER, sizeof(BMP_HEADER)) == 0) {
                sprintf(path, "%s/Pictures%s/album_artwork.bmp", cachePath, m_NowPlaying.libraryID.c_str());
            } else {
                sprintf(path, "%s/Pictures%s/album_artwork.jpg", cachePath, m_NowPlaying.libraryID.c_str());
            }

//            FILE *file = fopen(path, "wb+");
//            if (file != NULL) {
//                fwrite(transfer->data, transfer->total, 1, file);
//                fclose(file);
//            }else{
//            	CIpodLog::i("iAP2 File Transfer Session MediaItemArtworkFileTransferIdentifier NowPlayingArtworkData: %s failed.\n", path);
//            }
        }
        CIpodLog::i("iAP2 File Transfer Session MediaItemArtworkFileTransferIdentifier NowPlayingArtworkData: %s\n", path);
        CIpodEvent::sendEvent(IPOD_EVT_ALBUM_ARTWORK_CHANGED, getHandle(), path);
        m_NowPlaying.artworkPath = path;
        m_NowPlaying.artworkInited = true;
        break;
    }
    case IAP2FileTransferType::PLAYLIST: {
        uint32 totalnum = transfer->total / sizeof(uint64);
        uint64 itemids[totalnum];
        for (uint32 i = 0; i < totalnum; i++) {
            uint64 id;
            for (uint32 j = 0; j < 8; j++) {
                id = id * 256 + transfer->data[i * 8 + j];
            }
            itemids[i] = id;
        }
        if (transfer->libraryType == IAP2MediaLibraryType::LOCAL_DEVICE) {
            m_LocalDeviceSqlite.updateMediaPlaylist(transfer->playlistid, itemids, totalnum);
        } else if (transfer->libraryType == IAP2MediaLibraryType::MUSIC_RADIO) {
            m_MusicRadioSqlite.updateMediaPlaylist(transfer->playlistid, itemids, totalnum);
        }
        break;
    }
    case IAP2FileTransferType::PLAYING_LIST: {
        CIpodLog::i("CIpodIap2::saveFileTransfer, IAP2_FILE_TRANSFER_PLAYING_LIST\n");
        if( transfer->libraryType == IAP2MediaLibraryType::LOCAL_DEVICE )
        {
            m_FilePlayingLists.items.clear();
            m_FilePlayingLists.total = transfer->total / sizeof(uint64);
            uint32 currIndex = 0;
            for (uint32 i = 0; i < m_FilePlayingLists.total; i++) {
                uint64 id;
                for (uint32 j = 0; j < 8; j++) {
                    id = id * 256 + transfer->data[i * 8 + j];
                }
                CIpodLog::d("gaosheng id:%lu \n",id);
                m_FilePlayingLists.items.push_back(id);
                if(m_NowPlaying.id == id) currIndex = i;
            }
            m_FilePlayingLists.libraryType = transfer->libraryType;
            m_FilePlayingLists.playlistid = transfer->playlistid;
            CIpodEvent::sendEvent(IPOD_EVT_MEDIA_PLAYING_LIST_CHANGED, getHandle(),currIndex);
        }
        break;
    }
    }
    if (transfer->total > 0) {
        free(transfer->data);
    }
    for (std::list<IAP2FileTransfer *>::iterator iter = m_FileTransfers.begin(); iter != m_FileTransfers.end(); ) {
        if ((*iter)->id == transfer->id) {
            m_FileTransfers.erase(iter);
            free(transfer);
            break;
        } else {
            ++iter;
        }
    }
    return 0;
}

int CIpodIap2::parseEAData(const uint8 *data, uint16 len) {
    CIpodEvent::sendEvent(IPOD_EVT_EA_DATA_RECEIVED, len, data);
    return 0;
}

int CIpodIap2::notifyBtStatus(bool connected) {
    CIpodLog::i("CIpodIap2::notifyBtStatus\n");
    return bluetoothComponentInformation(connected);
}

int CIpodIap2::notifyUSBAudioStatus(bool enabled) {
    CIpodLog::i("CIpodIap2::notifyUSBAudioStatus, enabled = %d\n", enabled);
    int ret = -1;
    if (enabled) {
        ret = startUSBDeviceModeAudio();
    } else {
        ret = stopUSBDeviceModeAudio();
    }
    return ret;
}

int CIpodIap2::sendMediaCommand(int command) {
    CIpodLog::i("CIpodIap2::sendMediaCommand, command = %d\n", command);

    uint8 report[1] = { 0 };
    switch (command) {
    case IPODMediaCommand::PLAYPAUSE:
        report[0] = IAP2MediaCommand::PLAY_PAYUSE;
        break;
    case IPODMediaCommand::NEXT:
        report[0] = IAP2MediaCommand::NEXT;
        break;
    case IPODMediaCommand::SEEK_FORWARD:
        report[0] = IAP2MediaCommand::NEXT;
        break;
    case IPODMediaCommand::PREVIOUS:
        report[0] = IAP2MediaCommand::PREVIOUS;
        break;
    case IPODMediaCommand::SEEK_BACKWARD:
        report[0] = IAP2MediaCommand::PREVIOUS;
        break;
    case IPODMediaCommand::SEEKEND:
        report[0] = IAP2MediaCommand::UP;
        break;
    case IPODMediaCommand::PLAY:
        report[0] = IAP2MediaCommand::PLAY;
        break;
    case IPODMediaCommand::PAUSE:
        report[0] = IAP2MediaCommand::PAUSE;
        break;
    case IPODMediaCommand::SHUFFLE:
        report[0] = IAP2MediaCommand::SHUFFLE;
        break;
    case IPODMediaCommand::REPEAT:
        report[0] = IAP2MediaCommand::REPEAT;
        break;
    }
    accessoryHIDReport(m_MediaHID, report, 1);
    if (command != IPODMediaCommand::SEEK_FORWARD && command != IPODMediaCommand::SEEK_BACKWARD && command != IPODMediaCommand::SEEKEND) {
        report[0] = IAP2MediaCommand::UP;
        accessoryHIDReport(m_MediaHID, report, 1);
    }
    return 0;
}

int CIpodIap2::sendMediaCommandDown(int command) {
    CIpodLog::i("CIpodIap2::sendMediaCommandDown, command = %d\n", command);

    uint8 report[1] = { 0 };
    switch (command) {
    case IPODMediaCommand::PLAYPAUSE:
        report[0] = IAP2MediaCommand::PLAY_PAYUSE;
        break;
    case IPODMediaCommand::NEXT:
        report[0] = IAP2MediaCommand::NEXT;
        break;
    case IPODMediaCommand::SEEK_FORWARD:
        report[0] = IAP2MediaCommand::NEXT;
        break;
    case IPODMediaCommand::PREVIOUS:
        report[0] = IAP2MediaCommand::PREVIOUS;
        break;
    case IPODMediaCommand::SEEK_BACKWARD:
        report[0] = IAP2MediaCommand::PREVIOUS;
        break;
    case IPODMediaCommand::SEEKEND:
        report[0] = IAP2MediaCommand::UP;
        break;
    case IPODMediaCommand::PLAY:
        report[0] = IAP2MediaCommand::PLAY;
        break;
    case IPODMediaCommand::PAUSE:
        report[0] = IAP2MediaCommand::PAUSE;
        break;
    case IPODMediaCommand::SHUFFLE:
        report[0] = IAP2MediaCommand::SHUFFLE;
        break;
    case IPODMediaCommand::REPEAT:
        report[0] = IAP2MediaCommand::REPEAT;
        break;
    }
    accessoryHIDReport(m_MediaHID, report, 1);
    return 0;
}

int CIpodIap2::sendMediaCommandUp(int command) {
    CIpodLog::i("CIpodIap2::sendMediaCommandUp, command = %d\n", command);

    uint8 report[1] = { 0 };
    if (command != IPODMediaCommand::SEEK_FORWARD && command != IPODMediaCommand::SEEK_BACKWARD && command != IPODMediaCommand::SEEKEND) {
        report[0] = IAP2MediaCommand::UP;
        accessoryHIDReport(m_MediaHID, report, 1);
    }
    return 0;
}

int CIpodIap2::setPlayingTrackPosition(uint32 position) {
    CIpodLog::i("CIpodIap2::setPlayingTrackPosition, position = %lu\n", position);
    return setElapsedTime(position);
}

int CIpodIap2::getShuffleMode(int *mode) {
    if (mode == NULL) {
        CIpodLog::i("CIpodIap2::getShuffleMode, mode is null!\n");
        return -1;
    }

    while (1) {
        if (m_NowPlaying.inited) {
            *mode = m_NowPlaying.shuffleMode;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::i("CIpodIap2::getShuffleMode, mode = %d\n", *mode);
    return 0;
}

int CIpodIap2::getRepeatMode(int *mode) {
    if (mode == NULL) {
        CIpodLog::i("CIpodIap2::getRepeatMode, mode is null!\n");
        return -1;
    }

    while (1) {
        if (m_NowPlaying.inited) {
            *mode = m_NowPlaying.repeatMode;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::i("CIpodIap2::getRepeatMode, mode = %d\n", *mode);
    return 0;
}

int CIpodIap2::getPlayingTrackDuration(int * duration)
{
    if (duration == NULL)
    {
        CIpodLog::i("CIpodIap2::getPlayingTrackDuration, duration is null!\n");
        return -1;
    }
    while (1)
    {
        if (m_NowPlaying.inited) {
            *duration = m_NowPlaying.duration;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::d("CIpodIap2::getPlayingTrackDuration, duration = %d\n", *duration);
    return 0;
}

int CIpodIap2::getPlayingTrackTitle(std::string &title) {
    while (1) {
        if (m_NowPlaying.inited) {
            title = m_NowPlaying.title;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::d("CIpodIap2::getPlayingTrackTitle, title = %s\n", title.c_str());
    return 0;
}

int CIpodIap2::getPlayingTrackAlbum(std::string &album) {
    while (1) {
        if (m_NowPlaying.inited) {
            album = m_NowPlaying.album;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::d("CIpodIap2::getPlayingTrackAlbum, album = %s\n", album.c_str());
    return 0;
}

int CIpodIap2::getPlayingTrackArtist(std::string &artist) {
    while (1) {
        if (m_NowPlaying.inited) {
            artist = m_NowPlaying.artist;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::d("CIpodIap2::getPlayingTrackArtist, artist = %s\n", artist.c_str());
    return 0;
}

int CIpodIap2::getPlayingTrackAlbumArtwork() {
    CIpodLog::d("CIpodIap2::getPlayingTrackAlbumArtwork\n");

    if (m_NowPlaying.artworkInited) {
        CIpodEvent::sendEvent(IPOD_EVT_ALBUM_ARTWORK_CHANGED, getHandle(), m_NowPlaying.artworkPath.data());
    }
    return 0;
}

int CIpodIap2::getPlayStatus(IPODPlayStatus *status) {
    if (status == NULL) {
        CIpodLog::i("CIpodIap2::getPlayStatus, status is null!\n");
        return -1;
    }

    while (1) {
        if (m_NowPlaying.inited) {
            status->track_length = m_NowPlaying.duration;
            status->track_position = m_NowPlaying.position;
            status->state = m_NowPlaying.state;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::i("CIpodIap2::getPlayStatus, length = %lu, position = %lu, state = %d\n", status->track_length, status->track_position, status->state);
    return 0;
}

int CIpodIap2::getPlayingTrackInfo(int32 *index, uint32 *count) {
    if (index == NULL || count == NULL)
    {
        CIpodLog::i("CIpodIap2::getPlayingTrackInfo, index or count is null!\n");
        return -1;
    }

    while (1) {
        if (m_NowPlaying.inited) {
            *index = m_NowPlaying.index;
            *count = m_NowPlaying.count;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::i("CIpodIap2::getPlayingTrackInfo, index = %ld, count = %lu\n", *index, *count);
    return 0;
}

int CIpodIap2::getPlayingChapterInfo(int32 *index, uint32 *count) {
    if (index == NULL || count == NULL) {
        CIpodLog::i("CIpodIap2::getPlayingChapterInfo, index or count is null!\n");
        return -1;
    }

    while (1) {
        if (m_NowPlaying.inited) {
            *index = m_NowPlaying.chapterIndex;
            *count = m_NowPlaying.chapterCount;
            break;
        } else {
            usleep(2000);
        }
    }
    CIpodLog::i("CIpodIap2::getPlayingChapterInfo, index = %ld, count = %d\n", *index, *count);
    return 0;
}

int CIpodIap2::isListLevelSupported(bool *supported) {
    if (supported == NULL) {
        CIpodLog::i("CIpodIap2::isListLevelSupported, supported is null!\n");
        return -1;
    }

    *supported = true;
    CIpodLog::i("CIpodIap2::isListLevelSupported, supported = %d\n", *supported);
    return 0;
}

int CIpodIap2::resetMusicList(uint32 *num) {
    if (num == NULL) {
        CIpodLog::i("CIpodIap2::resetMusicList, num is null!\n");
        return -1;
    }

    while (m_DeviceLanguage.empty()) {
        usleep(10000);
    }
    CIpodLog::i("CIpodIap2::resetMusicList, device language inited\n");

    m_MediaLists.clear();
    IAP2MediaList mediaList;
    mediaList.category = IPODCategoryType::ALL;
    mediaList.count = MEDIA_TOP_LIST_SIZE;
    for(uint32 i = 0; i < mediaList.count; i++) {
        IPODMusicSource source;
        source.index = i + 1;
        source.id = i + 1;
        if (m_DeviceLanguage.compare(DEVICE_LANGUAGE_CHINESE) == 0) {
            source.name = MEDIA_TOP_LIST_CHINESE[i];
        } else {
            source.name = MEDIA_TOP_LIST_ENGLISH[i];
        }
        mediaList.items.insert(std::make_pair(i, source));
    }
    m_MediaLists.push_back(mediaList);
    if (num != NULL) {
        *num = mediaList.count;
    }
    CIpodLog::i("CIpodIap2::resetMusicList, num = %lu\n", *num);
    return 0;
}

int CIpodIap2::selectMusicList(int32 index, int32 level, uint32 *num) {
    CIpodLog::i("CIpodIap2::selectMusicList, index = %ld, level = %ld\n", index, level);

    if (num == NULL) {
        CIpodLog::i("CIpodIap2::selectMusicList, num is null!\n");
        return -1;
    }

    if (index == -1) {
        // 保留根目录
        if (m_MediaLists.size() == 0) {
            resetMusicList(num);
        } else {
            if (m_MediaLists.size() > 1) {
                m_MediaLists.pop_back();
            }
            std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
            if (iter != m_MediaLists.rend()) {
                *num = iter->count;
            }
        }
    } else {
        if (level > (int32)(m_MediaLists.size() - 1)) {
            CIpodLog::e("CIpodIap2::selectMusicList, this level has not been created! m_MediaLists.size = %d\n", m_MediaLists.size());
            return -1;
        } else {
            int32 selectLevel;
            if (level == -1) {
                selectLevel = (m_MediaLists.size() == 0) ? 0 : m_MediaLists.size() - 1;
            } else {
                selectLevel = level;
            }
            CIpodLog::i("CIpodIap2::selectMusicList, select level %ld\n", selectLevel);

            if (selectLevel == 0) {
                resetMusicList(NULL);
                IAP2MediaList mediaList;
                mediaList.parentID = index;
                switch (index) {
                case 0:     // 正在播放
                    CIpodLog::i("CIpodIap2::selectMusicList, this is now playing select!\n");
                    return -1;
                case 1:     // 播放列表
                    mediaList.category = IPODCategoryType::PLAYLIST;
                    m_LocalDeviceSqlite.getPlaylist(&mediaList);
                    break;
                case 2:     // 表演者
                    mediaList.category = IPODCategoryType::ARTIST;
                    m_LocalDeviceSqlite.getArtist(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 3:     // 专辑
                    mediaList.category = IPODCategoryType::ALBUM;
                    m_LocalDeviceSqlite.getAlbum(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 4:     // 风格
                    mediaList.category = IPODCategoryType::GENRE;
                    m_LocalDeviceSqlite.getGenre(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 5:     // 歌曲
                    mediaList.category = IPODCategoryType::SONGS;
                    m_LocalDeviceSqlite.getSongs(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 6:     // 作曲者
                    mediaList.category = IPODCategoryType::COMPOSER;
                    m_LocalDeviceSqlite.getComposer(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 7:     // 有声读物
                    mediaList.category = IPODCategoryType::AUDIOBOOK;
                    m_LocalDeviceSqlite.getAudioBook(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 8:     // 播客
                    mediaList.category = IPODCategoryType::PODCAST;
                    m_LocalDeviceSqlite.getPodcast(&mediaList, m_LocalDeviceLibrary.hideRemoteItems);
                    break;
                case 9:     // 电台
                    mediaList.category = IPODCategoryType::RADIO;
                    m_MusicRadioSqlite.getiTunesRadio(&mediaList);
                    break;
                }
                *num = mediaList.count;
                m_MediaLists.push_back(mediaList);
            } else if (selectLevel == 1) {
                while (m_MediaLists.size() > 2) {
                    m_MediaLists.pop_back();
                }
                IAP2MediaList mediaList;
                std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
                if (iter != m_MediaLists.rend()) {
                    mediaList.parentID = iter->items[index].id;
                    switch (iter->category) {
                    case IPODCategoryType::ALL:
                        return -1;
                    case IPODCategoryType::PLAYLIST:
                        mediaList.category = IPODCategoryType::SONGS;
                        m_LocalDeviceSqlite.getSongsByPlaylist(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::ARTIST:
                        mediaList.category = IPODCategoryType::ALBUM;
                        m_LocalDeviceSqlite.getAlbumByArtist(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::ALBUM:
                        mediaList.category = IPODCategoryType::SONGS;
                        m_LocalDeviceSqlite.getSongsByAlbum(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::GENRE:
                        mediaList.category = IPODCategoryType::ARTIST;
                        m_LocalDeviceSqlite.getArtistByGenre(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::SONGS: {
                        uint64 itemIDs[iter->count];
                        for (uint32 i = 0; i < iter->count; i++) {
                            itemIDs[i] = iter->items[i].id;
                        }
                        playMediaLibraryItems(m_LocalDeviceLibrary.id, itemIDs, iter->count, index);
                        *num = iter->count;
                        return 0;
                    }
                    case IPODCategoryType::COMPOSER:
                        mediaList.category = IPODCategoryType::ALBUM;
                        m_LocalDeviceSqlite.getAlbumByComposer(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::AUDIOBOOK:
                        mediaList.category = IPODCategoryType::SONGS;
                        m_LocalDeviceSqlite.getSongsByAudioBook(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::PODCAST:
                        mediaList.category = IPODCategoryType::SONGS;
                        m_LocalDeviceSqlite.getSongsByPodcast(&mediaList, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        break;
                    case IPODCategoryType::RADIO:
                        playMediaLibraryCollection(m_MusicRadioLibrary.id, mediaList.parentID, IAP2MediaCollectionType::PLAYLIST, 0);
                        *num = iter->count;
                        return 0;
                    }
                    *num = mediaList.count;
                    m_MediaLists.push_back(mediaList);
                }
            } else if (selectLevel == 2) {
                while (m_MediaLists.size() > 3) {
                    m_MediaLists.pop_back();
                }
                std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
                if (iter != m_MediaLists.rend()) {
                    if (iter->category == IPODCategoryType::ARTIST) {
                        IAP2MediaList mediaList;
                        mediaList.parentID = iter->items[index].id;
                        mediaList.category = IPODCategoryType::ALBUM;
                        m_LocalDeviceSqlite.getAlbumByGenreArtist(&mediaList, iter->parentID, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        *num = mediaList.count;
                        m_MediaLists.push_back(mediaList);
                    } else if (iter->category == IPODCategoryType::ALBUM) {
                        IAP2MediaList mediaList;
                        mediaList.category = IPODCategoryType::SONGS;
                        mediaList.parentID = iter->items[index].id;
                        uint64 parentID = iter->parentID;
                        ++iter;
                        if (iter->category == IPODCategoryType::ARTIST) {
                            m_LocalDeviceSqlite.getSongsByArtistAlbum(&mediaList, parentID, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        } else if (iter->category == IPODCategoryType::COMPOSER) {
                            m_LocalDeviceSqlite.getSongsByComposerAlbum(&mediaList, parentID, mediaList.parentID, m_LocalDeviceLibrary.hideRemoteItems);
                        }
                        *num = mediaList.count;
                        m_MediaLists.push_back(mediaList);
                    } else if (iter->category == IPODCategoryType::SONGS) {
                        uint32 itemCount = iter->count;
                        uint64 itemIDs[itemCount];
                        for (uint32 i = 0; i < iter->count; i++) {
                            itemIDs[i] = iter->items[i].id;
                        }
                        playMediaLibraryItems(m_LocalDeviceLibrary.id, itemIDs, itemCount, index);
                        *num = itemCount;
                    }
                }
            } else if (selectLevel == 3) {
                while (m_MediaLists.size() > 4) {
                    m_MediaLists.pop_back();
                }

                std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
                if (iter != m_MediaLists.rend()) {
                    if (iter->category == IPODCategoryType::ALBUM) {
                        IAP2MediaList mediaList;
                        mediaList.parentID = iter->items[index].id;
                        mediaList.category = IPODCategoryType::SONGS;
                        uint64 album_id = mediaList.parentID;
                        uint64 artist_id = iter->parentID;
                        ++iter;
                        uint64 genre_id = iter->parentID;
                        m_LocalDeviceSqlite.getSongsByGenreArtistAlbum(&mediaList, genre_id, artist_id, album_id, m_LocalDeviceLibrary.hideRemoteItems);
                        *num = mediaList.count;
                        m_MediaLists.push_back(mediaList);
                    } else if (iter->category == IPODCategoryType::SONGS) {
                        uint64 itemIDs[iter->count];
                        for (uint32 i = 0; i < iter->count; i++) {
                            itemIDs[i] = iter->items[i].id;
                        }
                        playMediaLibraryItems(m_LocalDeviceLibrary.id, itemIDs, iter->count, index);
                        *num = iter->count;
                    }
                }
            } else if (selectLevel == 4) {
                std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
                if (iter != m_MediaLists.rend()) {
                    uint64 itemIDs[iter->count];
                    for (uint32 i = 0; i < iter->count; i++) {
                        itemIDs[i] = iter->items[i].id;
                    }
                    playMediaLibraryItems(m_LocalDeviceLibrary.id, itemIDs, iter->count, index);
                    *num = iter->count;
                }
            }
        }
    }
    CIpodLog::i("CIpodIap2::selectMusicList, index = %ld, level = %ld, num = %lu\n", index, level, *num);
    return 0;
}

int CIpodIap2::getMusicList(uint32 startIndex, uint32 readCount, IPODMusicList *musicList) {
    if (musicList == NULL) {
        CIpodLog::i("CIpodIap2::getMusicList, musicList is null!\n");
        return -1;
    }

    std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
    if (iter != m_MediaLists.rend()) {
        if (startIndex > iter->count) {
            CIpodLog::e("CIpodIap2::getMusicList, startIndex is greater than top list size!\n");
            return -1;
        }
        if (startIndex + readCount > iter->count) {
            readCount = iter->count - startIndex;
        }
        for (uint32 i = 0; i < readCount; i++) {
            IPODMusicSource source;
            source.index = iter->items[i + startIndex].index;
            source.id = iter->items[i + startIndex].id;
            if (iter->items[i + startIndex].name.empty()) {
                if (m_DeviceLanguage.compare(DEVICE_LANGUAGE_CHINESE) == 0) {
                    source.name = IPOD_ITEM_DEFAULT_NAME_CHINESE;
                } else {
                    source.name = IPOD_ITEM_DEFAULT_NAME_ENGLISH;
                }
            } else {
                source.name = iter->items[i + startIndex].name;
            }
            musicList->elements.push_back(source);
        }
        musicList->size = readCount;
        musicList->start = startIndex;
        musicList->end = ((readCount > 0)?(startIndex + musicList->size - 1):0);
        musicList->categorytype = iter->category;
        musicList->totalnum = iter->count;
        musicList->level = m_MediaLists.size() - 1;
    }

    CIpodLog::i("CIpodIap2::getMusicList, startIndex = %lu, readCount = %lu\n", startIndex, readCount);
    return 0;
}

int CIpodIap2::getMusicList_c(uint32 startIndex, uint32 readCount, C_IPODMusicList *musicList) {
    if (musicList == NULL) {
        CIpodLog::i("CIpodIap2::getMusicList, musicList is null!\n");
        return -1;
    }

    std::list<IAP2MediaList>::reverse_iterator iter = m_MediaLists.rbegin();
    if (iter != m_MediaLists.rend()) {
        if (startIndex > iter->count) {
            CIpodLog::e("CIpodIap2::getMusicList, startIndex is greater than top list size!\n");
            return -1;
        }
        if (startIndex + readCount > iter->count) {
            readCount = iter->count - startIndex;
        }
        for (uint32 i = 0; i < readCount; i++) {

            musicList->elements[i].index = iter->items[i + startIndex].index;
            musicList->elements[i].id = iter->items[i + startIndex].id;
            if (iter->items[i + startIndex].name.empty()) {
                if (m_DeviceLanguage.compare(DEVICE_LANGUAGE_CHINESE) == 0) {
                    memcpy(musicList->elements[i].name,IPOD_ITEM_DEFAULT_NAME_CHINESE,sizeof(IPOD_ITEM_DEFAULT_NAME_CHINESE));
                } else {
                    memcpy(musicList->elements[i].name,IPOD_ITEM_DEFAULT_NAME_ENGLISH,sizeof(IPOD_ITEM_DEFAULT_NAME_ENGLISH));
                }
            } else {
                memcpy(musicList->elements[i].name,iter->items[i + startIndex].name.c_str(),sizeof(iter->items[i + startIndex].name));
            }
        }
        musicList->size = readCount;
        musicList->start = startIndex;
        musicList->end = ((readCount > 0)?(startIndex + musicList->size - 1):0);
        musicList->categorytype = iter->category;
        musicList->totalnum = iter->count;
        musicList->level = m_MediaLists.size() - 1;
    }

    CIpodLog::i("CIpodIap2::getMusicList, startIndex = %lu, readCount = %lu\n", startIndex, readCount);
    return 0;
}

int CIpodIap2::getMusicPlayingList(uint32 startIndex, uint32 readCount, C_IPODMusicList *musicList) {
    if (musicList == NULL) {
        CIpodLog::e("CIpodIap2::getMusicPlayingList, musicList is null!\n");
        return -1;
    }
    if (startIndex >= m_FilePlayingLists.total) {
        CIpodLog::e("CIpodIap2::getMusicPlayingList, startIndex is greater than top list size!\n");
        return -1;
    }
    if(m_FilePlayingLists.libraryType != IAP2MediaLibraryType::LOCAL_DEVICE){
        CIpodLog::e("CIpodIap2::getMusicPlayingList, m_FilePlayingLists.libraryType is not LOCAL_DEVICE!\n");
        return -1;
    }

    if (startIndex + readCount > m_FilePlayingLists.total) {
        readCount = m_FilePlayingLists.total - startIndex;
    }

    for (uint32 i = 0; i < readCount; i++) {
        IAP2MediaList mediaListTemp;
        bool flag = false;
        uint32 n_index = 0;

        for (std::list<uint64>::iterator iter = m_FilePlayingLists.items.begin(); iter != m_FilePlayingLists.items.end(); ++iter) {
            if(n_index == startIndex + i){
                m_LocalDeviceSqlite.getSongsByID(&mediaListTemp,*iter, m_LocalDeviceLibrary.hideRemoteItems);
                flag = true;
                break;
            }
            n_index++;
        }
        if(flag){
            musicList->elements[i].index = i;
            musicList->elements[i].id = mediaListTemp.items[0].id;
            memset( musicList->elements[i].name, 0, 128 );
            if (mediaListTemp.items[0].name.empty()) {
                if (m_DeviceLanguage.compare(DEVICE_LANGUAGE_CHINESE) == 0) {
                    memcpy(musicList->elements[i].name,IPOD_ITEM_DEFAULT_NAME_CHINESE,sizeof(IPOD_ITEM_DEFAULT_NAME_CHINESE));
                } else {
                    memcpy(musicList->elements[i].name,IPOD_ITEM_DEFAULT_NAME_ENGLISH,sizeof(IPOD_ITEM_DEFAULT_NAME_ENGLISH));
                }
            } else {
                memcpy(musicList->elements[i].name,mediaListTemp.items[0].name.c_str(),strlen( mediaListTemp.items[0].name.c_str() ) );
            }
            CIpodLog::i("GetMusicPlayingList( iAP2 ) index: %d, id: %lu, name: %s\n", musicList->elements[i].index, musicList->elements[i].id,musicList->elements[i].name);
        }else{
            readCount = i;
            CIpodLog::e("CIpodIap2::getMusicPlayingList, list do not exist!\n");
            break;
        }
    }
    musicList->size = readCount;
    musicList->start = startIndex;
    musicList->end = ((readCount > 0)?(startIndex + readCount - 1):0);
    musicList->categorytype = IPODCategoryType::SONGS;
    musicList->totalnum = m_FilePlayingLists.total;
    musicList->level = 2;

    CIpodLog::i("GetMusicPlayingList startIndex: %lu, readCount: %lu, totalnum: %d.\n", startIndex, readCount,musicList->totalnum);
    return 0;
}

int CIpodIap2::selectMusicPlayingList(int32 index) {
    CIpodLog::i("CIpodIap2::selectMusicPlayingList, index = %lu total:%d \n", index ,m_FilePlayingLists.total);

    int32 count = m_FilePlayingLists.total;
    if(count == 0  || (index + 1 > count)){
        CIpodLog::e("CIpodIap2::selectMusicPlayingList, musicPlayingList is null!\n");
        return -1;
    }

    if(m_FilePlayingLists.libraryType == IAP2MediaLibraryType::LOCAL_DEVICE){
        uint64 itemIDs[count];
        uint32 n_index = 0;
        for (std::list<uint64>::iterator iter = m_FilePlayingLists.items.begin(); iter != m_FilePlayingLists.items.end(); ++iter) {
            if(n_index < count){
                CIpodLog::d("gaosheng id:%lu \n",*iter);
                itemIDs[n_index] = *iter;
            }else{
                break;
            }
            n_index ++;
        }
        playMediaLibraryItems(m_LocalDeviceLibrary.id, itemIDs, count, index);
    }else{
        CIpodLog::e("CIpodIap2::selectMusicPlayingList, m_FilePlayingLists.libraryType is not LOCAL_DEVICE!\n");
    }

    CIpodLog::i("CIpodIap2::selectMusicPlayingList, index = %lu\n", index);
    return 0;
}

int CIpodIap2::launchApp(const char *appBundleID) {
    return requestAppLaunch(appBundleID);
}

int CIpodIap2::setLocationInformation(const char *sentence){
    return locationInformation(sentence);
}

int CIpodIap2::sendEAData(const uint8 *data, uint16 len) {
    int ret = -1;
    if (CIpodConfig::getIdentification()->eaprotocol.nativeid > 0) {
        ret = send(data, len, 1);
    } else {
        if (m_EAPStatus.sessionID == 0) {
            CIpodLog::i("CIpodIap2::sendEAData, protocol not exist!\n");
            return -1;
        }
        uint8 payload[len + 2];
        payload[0] = (uint8) m_EAPStatus.sessionID >> 8;
        payload[1] = (uint8) m_EAPStatus.sessionID;
        memcpy(payload + 2, data, len);

        IAP2_CONTROL_BYTE control;
        control.value = 0x00;
        control.ACK = 1;
        IAP2_LINK_PACKET_OUT linkPacketOut;
        increaseSyncNumber();
        uint16 packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_EA, payload, len + 2, &linkPacketOut);
        ret = write((uint8 *)&linkPacketOut, packetSize);
    }
    return ret;
}

int CIpodIap2::requestAuthenticationCertificate(const uint8 *data) {

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID)
        {
            case IAP2_AA_RAC_RequestAuthenticationCertificateSerialNumber:
                m_ReqSerialNumber = true;
                CIpodLog::i("[  Device   ] step 9:  ACK RequestAuthenticationCertificateSerialNumber.\n");
                break;
        }
        parsedLen += paramLen;
    }

    if(m_ReqSerialNumber){
        m_ReqSerialNumber = false;
        return accessoryAuthenticationSerialNumber();
    }

    CIpodLog::i("[  Device   ] step 11: ACK RequestAuthenticationCertificate.\n");

    size_t			certificateLen;
	uint8_t *		certificatePtr = NULL;
    uint8_t *       certificateSNPtr = NULL;

    CIpodChip::MFiPlatform_CopyCertificate( &certificateSNPtr, &certificatePtr, &certificateLen );

    uint16 debugLen = certificateLen;
    std::string buf;
    for (uint16 i = 0; i < debugLen; i++) {
        char temp[10];
        sprintf(temp, "%02X ", certificatePtr[i]);
        buf.append(temp);
    }
    buf.append("\n");
    CIpodLog::i("certificatePtr %s", buf.data());

    return authenticationCertificate( certificatePtr, certificateLen );
}

int CIpodIap2::authenticationCertificate(const uint8 *certificate, uint16 len) {
    CIpodLog::i("[ Accessory ] step 12: ACK AuthenticationCertificate 'X.509 Certificate' Length: %d.\n", len );

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    packetSize = createParamPacket(IAP2_AA_AC_AuthenticationCertificate, certificate, len, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_ACCESSORY_AUTHENTICATION, IAP2_AA_AuthenticationCertificate, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::requestAuthenticationChallengeResponse(const uint8 *data) {
    
    int ret = -1;
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    uint8 challengeBuf[128] = { 0 };
    uint16 challengeLen = 0;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        // CIpodLog::i("CIpodIap2::requestAuthenticationChallengeResponse paramID = %d, paramLen = %d\n", paramID, paramLen);
        switch (paramID) {
        case IAP2_AA_RACR_AuthenticationChallenge:
            challengeLen = paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
            memcpy(challengeBuf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, challengeLen);
            break;
        }
        parsedLen += paramLen;
    }

    size_t			signatureLen;
	uint8_t *		signaturePtr = NULL;

    CIpodLog::i("[  Device   ] step 13: ACK RequestAuthenticationChallengeResponse 'Challenge Data' Length: %d.\n", challengeLen );

    CIpodChip::MFiPlatform_CreateSignature( challengeBuf, challengeLen, &signaturePtr, &signatureLen );

    ret = authenticationResponse( signaturePtr, signatureLen );

    free( signaturePtr );

    return ret;
}

int CIpodIap2::authenticationResponse(const uint8 *response, uint16 len) {
    CIpodLog::i("[ Accessory ] step 14: ACK AuthenticationResponse 'Challenge Response Data' Length: %d.\n", len );

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    packetSize = createParamPacket(IAP2_AA_AC_AuthenticationCertificate, response, len, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_ACCESSORY_AUTHENTICATION, IAP2_AA_AuthenticationResponse,
            paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::authenticationFailed() {
    CIpodLog::i("CIpodIap2::authenticationFailed\n");

    m_AuthenticationState = IAP2AuthenticationState::FAILED;
    return 0;
}

int CIpodIap2::authenticationSucceeded() {
    CIpodLog::i("[  Device   ] step 15: ACK AuthenticationSucceeded\n");

    m_AuthenticationState = IAP2AuthenticationState::SUCCEEDED;
    return 0;
}

int CIpodIap2::accessoryAuthenticationSerialNumber()
{
    uint16 len = 32;

    size_t			certificateLen;
	uint8_t *		certificatePtr = NULL;
    uint8_t *       certificateSNPtr = NULL;

    CIpodChip::MFiPlatform_CopyCertificate( &certificateSNPtr, &certificatePtr, &certificateLen );

    CIpodLog::i("[ Accessory ] step 10: ACK AccessoryAuthenticationSerialNumber 'X.509 Certificate Serial Number': %s.\n", certificateSNPtr );

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    packetSize = createParamPacket(IAP2_AA_AC_AuthenticationCertificate, certificateSNPtr, len, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_ACCESSORY_AUTHENTICATION, IAP2_AA_AccessoryAuthenticationSerialNumber,
            paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startIdentification() {
    CIpodLog::i("[  Device   ] step 6:  ACK StartIdentification\n");

    return identificationInformation();
}

int CIpodIap2::identificationInformation() {
    CIpodLog::i("[ Accessory ] step 7:  ACK IdentificationInformation\n");

    uint8 data[1024] = { 0 };
    uint8 subdata[1024] = { 0 };
    uint8 tempdata[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    uint16 point = 0;
    uint16 subpoint = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;

    IPODIdentification *identification = CIpodConfig::getIdentification();

    // IAP2_ACCESSORY_NAME
    paramLen = createParamPacket(IAP2_AI_II_Name, (uint8 *)identification->name.c_str(), identification->name.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_ModelIdentifier
    paramLen = createParamPacket(IAP2_AI_II_ModelIdentifier, (uint8 *)identification->modelIdentifier.c_str(), identification->modelIdentifier.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_Manufacturer
    paramLen = createParamPacket(IAP2_AI_II_Manufacturer, (uint8 *)identification->manufacturer.c_str(), identification->manufacturer.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_SerialNumber
#ifdef IAP2_SERIAL_NUMBER_BY_BTADDRESS
    char serialNumber[128];
    const uint8 *btaddress = CIpodVehicle::getBtAddress();
    sprintf(serialNumber, "%2x%2x%2x%2x%2x%2x", btaddress[0], btaddress[1], btaddress[2], btaddress[3], btaddress[4], btaddress[5]);
    CIpodLog::i("serialNumber = %s, strlen(serialNumber) = %d\n", serialNumber, strlen(serialNumber));
    paramLen = createParamPacket(IAP2_AI_II_SerialNumber, (uint8 *)serialNumber, strlen(serialNumber) + 1, data);
#else
    paramLen = createParamPacket(IAP2_AI_II_SerialNumber, (uint8 *)identification->serialNumber.c_str(), identification->serialNumber.length() + 1, data);
#endif
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_FirmwareVersion
    paramLen = createParamPacket(IAP2_AI_II_FirmwareVersion, (uint8 *)identification->firmwareVersion.c_str(), identification->firmwareVersion.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_HardwareVersion
    paramLen = createParamPacket(IAP2_AI_II_HardwareVersion, (uint8 *)identification->hardwareVersion.c_str(), identification->hardwareVersion.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_MessagesSentByAccessory
    uint16 sentPos = 0;
    uint16 msgSent[m_MessagesSent.size()];
    for (std::list<uint16>::iterator iter = m_MessagesSent.begin(); iter != m_MessagesSent.end(); ++iter)
    {
        msgSent[sentPos++] = *iter;
    }
    paramLen = createParamPacket(IAP2_AI_II_MessagesSentByAccessory, (uint8 *)msgSent, m_MessagesSent.size() * 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_MessagesReceivedFromDevice
    int receivedPos = 0;
    uint16 msgReceived[m_MessagesReceived.size()];
    for (std::list<uint16>::iterator iter = m_MessagesReceived.begin(); iter != m_MessagesReceived.end(); ++iter)
    {
        msgReceived[receivedPos++] = *iter;
    }
    paramLen = createParamPacket(IAP2_AI_II_MessagesReceivedFromDevice, (uint8 *) msgReceived, m_MessagesReceived.size() * 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_PowerSourceType
    paramLen = createParamPacket(IAP2_AI_II_PowerProvidingCapability, (uint8 *) &(identification->power), 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_MaximumCurrentDrawnFromDevice
    uint16 maxDrawn = IAP2_BIG_ENDIAN_SWITCH(identification->maxCurrDrawn);
    paramLen = createParamPacket(IAP2_AI_II_MaximumCurrentDrawnFromDevice, (uint8 *) &maxDrawn, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_SupportedExternalAccessoryProtocol
    if (identification->eaprotocol.id > 0) {
        subpoint = 0;

        paramLen = createParamPacket(IAP2_AI_II_EAP_ExternalAccessoryProtocolIdentifier, (uint8 *)&identification->eaprotocol.id, 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_EAP_ExternalAccessoryProtocolName,(uint8 *)identification->eaprotocol.name.c_str(), identification->eaprotocol.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_EAP_ExternalAccessoryProtocolMatchAction, (uint8 *)&identification->eaprotocol.action, 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->eaprotocol.nativeid > 0) {
            uint16 nativeID = IAP2_BIG_ENDIAN_SWITCH(identification->eaprotocol.nativeid);
            paramLen = createParamPacket(IAP2_AI_II_EAP_NativeTransportComponentIdentifier, (uint8 *)&nativeID, 2, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->eaprotocol.carplay) {
            paramLen = createParamPacket(IAP2_AI_II_EAP_ExternalAccessoryProtocolCarPlay, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_SupportedExternalAccessoryProtocol, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_AppMatchTeamID
    if (!identification->appTeamId.empty()) {
        paramLen = createParamPacket(IAP2_AI_II_AppMatchTeamID,(uint8 *)identification->appTeamId.c_str(), identification->appTeamId.length() + 1, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_CurrentLanguage
    paramLen = createParamPacket(IAP2_AI_II_CurrentLanguage,(uint8 *)identification->currentLanguage.c_str(), identification->currentLanguage.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // IAP2_AI_II_SupportedLanguage
    for (std::list<std::string>::iterator iter = identification->supportedLanguages.begin(); iter != identification->supportedLanguages.end(); ++iter) {
        paramLen = createParamPacket(IAP2_AI_II_SupportedLanguage, (uint8 *)iter->data(), iter->length() + 1, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_SerialTransportComponent
    if (identification->serialTransport.id > 0) {
        subpoint = 0;

        uint16 serialID = IAP2_BIG_ENDIAN_SWITCH(identification->serialTransport.id);
        paramLen = createParamPacket(IAP2_AI_II_STC_TransportComponentIdentifier, (uint8 *)&serialID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_STC_TransportComponentName,(uint8 *)identification->serialTransport.name.c_str(), identification->serialTransport.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->serialTransport.connection) {
            paramLen = createParamPacket(IAP2_AI_II_STC_TransportSupportsiAP2Connection, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_SerialTransportComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_USBDeviceTransportComponent
    if (identification->usbDeviceTransport.id > 0) {
        subpoint = 0;

        uint16 usbDeviceID = IAP2_BIG_ENDIAN_SWITCH(identification->usbDeviceTransport.id);
        paramLen = createParamPacket(IAP2_AI_II_USBDTC_TransportComponentIdentifier, (uint8 *)&usbDeviceID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_USBDTC_TransportComponentName,(uint8 *)identification->usbDeviceTransport.name.c_str(), identification->usbDeviceTransport.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->usbDeviceTransport.connection) {
            paramLen = createParamPacket(IAP2_AI_II_USBDTC_TransportSupportsiAP2Connection, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        for (std::list<int>::iterator iter = identification->usbDeviceTransport.rates.begin(); iter != identification->usbDeviceTransport.rates.end(); ++iter) {
            paramLen = createParamPacket(IAP2_AI_II_USBDTC_USBDeviceSupportedAudioSampleRate, (uint8 *)&(*iter), 1, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_USBDeviceTransportComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_USBHostTransportComponent
    if (identification->usbHostTransport.id > 0) {
        subpoint = 0;

        uint16 usbHostID = IAP2_BIG_ENDIAN_SWITCH(identification->usbHostTransport.id);
        paramLen = createParamPacket(IAP2_AI_II_USBHTC_TransportComponentIdentifier, (uint8 *)&usbHostID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_USBHTC_TransportComponentName,(uint8 *)identification->usbHostTransport.name.c_str(), identification->usbHostTransport.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->usbHostTransport.connection) {
            paramLen = createParamPacket(IAP2_AI_II_USBHTC_TransportSupportsiAP2Connection, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->usbHostTransport.number > 0) {
            paramLen = createParamPacket(IAP2_AI_II_USBHTC_USBHostTransportCarPlayInterfaceNumber, (uint8 *)&(identification->usbHostTransport.number), 1, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->usbHostTransport.carplay) {
            paramLen = createParamPacket(IAP2_AI_II_USBHTC_TransportSupportsCarPlay, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_USBHostTransportComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_BluetoothTransportComponent
    if (identification->bluetoothTransport.id > 0) {
        subpoint = 0;

        uint16 bluetoothID = IAP2_BIG_ENDIAN_SWITCH(identification->bluetoothTransport.id);
        paramLen = createParamPacket(IAP2_AI_II_BTC_TransportComponentIdentifier, (uint8 *)&bluetoothID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_BTC_TransportComponentName, (uint8 *)identification->bluetoothTransport.name.c_str(), identification->bluetoothTransport.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->bluetoothTransport.connection) {
            paramLen = createParamPacket(IAP2_AI_II_BTC_TransportSupportsiAP2Connection, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        //02 0D 7A 42 42 92
//        uint8 btAddress[6] = {0x02,0x0D,0x7A,0x42,0x42,0x92};
//        paramLen = createParamPacket(IAP2_AI_II_BTC_BluetoothTransportMediaAccessControlAddress, btAddress, 6, subdata);
        paramLen = createParamPacket(IAP2_AI_II_BTC_BluetoothTransportMediaAccessControlAddress, CIpodVehicle::getBtAddress(), 6, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_BluetoothTransportComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_iAP2HIDComponent
    for (std::list<USBDeviceHIDComponent>::iterator iter = identification->usbDeviceHIDs.begin(); iter != identification->usbDeviceHIDs.end(); ++iter) {
        subpoint = 0;

        uint16 usbDeviceHIDID = IAP2_BIG_ENDIAN_SWITCH(iter->id);
        paramLen = createParamPacket(IAP2_AI_II_HIDC_HIDComponentIdentifier, (uint8 *)&usbDeviceHIDID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_HIDC_HIDComponentName, (uint8 *)iter->name.c_str(), iter->name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_HIDC_HIDComponentFunction, (uint8 *)&(iter->function), 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_iAP2HIDComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_VehicleInformationComponent
    if (identification->vehicleInfo.id > 0) {
        subpoint = 0;

        uint16 vehicleInfoID = IAP2_BIG_ENDIAN_SWITCH(identification->vehicleInfo.id);
        paramLen = createParamPacket(IAP2_AI_II_VIC_Identifier, (uint8 *)&vehicleInfoID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_VIC_Name, (uint8 *)identification->vehicleInfo.name.c_str(), identification->vehicleInfo.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        for (std::list<int>::iterator iter = identification->vehicleInfo.engines.begin(); iter != identification->vehicleInfo.engines.end(); ++iter) {
            paramLen = createParamPacket(IAP2_AI_II_VIC_EngineType, (uint8 *)&(*iter), 1, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_VIC_DisplayName, (uint8 *)identification->vehicleInfo.display.c_str(), identification->vehicleInfo.display.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_VehicleInformationComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_VehicleStatusComponent
    if (identification->vehicleStatus.id > 0) {
        subpoint = 0;

        uint16 vehicleStatusID = IAP2_BIG_ENDIAN_SWITCH(identification->vehicleStatus.id);
        paramLen = createParamPacket(IAP2_AI_II_VSC_Identifier, (uint8 *)&vehicleStatusID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_VSC_Name, (uint8 *)identification->vehicleStatus.name.c_str(), identification->vehicleStatus.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->vehicleStatus.range == 1) {
            paramLen = createParamPacket(IAP2_AI_II_VSC_Range, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->vehicleStatus.temperature == 1) {
            paramLen = createParamPacket(IAP2_AI_II_VSC_OutsideTemperature, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->vehicleStatus.warning == 1) {
            paramLen = createParamPacket(IAP2_AI_II_VSC_RangeWarning, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_VehicleStatusComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_LocationInformationComponent
    if (identification->locationInfo.id > 0) {
        subpoint = 0;

        uint16 locationID = IAP2_BIG_ENDIAN_SWITCH(identification->locationInfo.id);
        paramLen = createParamPacket(IAP2_AI_II_LIC_Identifier, (uint8 *)&locationID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_LIC_Name, (uint8 *)identification->locationInfo.name.c_str(), identification->locationInfo.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (identification->locationInfo.GPGGA == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_GlobalPositioningSystemFixData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.GPRMC == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_RecommendedMinimumSpecificGPSTransitData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.GPGSV == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_GPSSatellitesInView, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.PASCD == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_VehicleSpeedData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.PAGCD == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_VehicleGyroData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.PAACD == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_VehicleAccelerometerData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (identification->locationInfo.GPHDT == 1) {
            paramLen = createParamPacket(IAP2_AI_II_LIC_VehicleHeadingData, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_LocationInformationComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_USBHostHIDComponent
    for (std::list<USBHostHIDComponent>::iterator iter = identification->usbHostHIDs.begin(); iter != identification->usbHostHIDs.end(); ++iter) {
        subpoint = 0;

        uint16 usbHostHIDID = IAP2_BIG_ENDIAN_SWITCH(iter->id);
        paramLen = createParamPacket(IAP2_AI_II_USBHHIDC_HIDComponentIdentifier, (uint8 *)&usbHostHIDID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_USBHHIDC_HIDComponentName, (uint8 *)iter->name.c_str(), iter->name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_USBHHIDC_HIDComponentFunction, (uint8 *)&(iter->function), 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 transportID = IAP2_BIG_ENDIAN_SWITCH(iter->transportid);
        paramLen = createParamPacket(IAP2_AI_II_USBHHIDC_USBHostTransportComponentIdentifier, (uint8 *)&transportID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 interfaceNumber = IAP2_BIG_ENDIAN_SWITCH(iter->number);
        paramLen = createParamPacket(IAP2_AI_II_USBHHIDC_USBHostTransportInterfaceNumber, (uint8 *)&interfaceNumber, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_USBHostHIDComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_WirelessCarPlayTransportComponent
    for (std::list<WirelessCarPlayTransportComponent>::iterator iter = identification->wirelessCarPlayTransports.begin(); iter != identification->wirelessCarPlayTransports.end(); ++iter) {
        subpoint = 0;

        uint16 transportID = IAP2_BIG_ENDIAN_SWITCH(iter->id);
        paramLen = createParamPacket(IAP2_AI_II_WCTC_TransportComponentIdentifier, (uint8 *)&transportID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_WCTC_TransportComponentName, (uint8 *)iter->name.c_str(), iter->name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        if (iter->connection) {
            paramLen = createParamPacket(IAP2_AI_II_WCTC_TransportSupportsiAP2Connection, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        if (iter->carplay) {
            paramLen = createParamPacket(IAP2_AI_II_WCTC_TransportSupportsCarPlay, NULL, 0, subdata);
            memcpy(tempdata + subpoint, subdata, paramLen);
            subpoint += paramLen;
        }

        paramLen = createParamPacket(IAP2_AI_II_WirelessCarPlayTransportComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_BluetoothHIDComponent
    if (identification->bluetoothHID.id > 0) {
        subpoint = 0;

        uint16 bluetoothHIDID = IAP2_BIG_ENDIAN_SWITCH(identification->bluetoothHID.id);
        paramLen = createParamPacket(IAP2_AI_II_BHIDC_HIDComponentIdentifier, (uint8 *)&bluetoothHIDID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_BHIDC_HIDComponentName, (uint8 *)identification->bluetoothHID.name.c_str(), identification->bluetoothHID.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_BHIDC_HIDComponentFunction, (uint8 *)&(identification->bluetoothHID.function), 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 transportID = IAP2_BIG_ENDIAN_SWITCH(identification->bluetoothHID.transportid);
        paramLen = createParamPacket(IAP2_AI_II_BHIDC_BluetoothTransportComponentIdentifier, (uint8 *)&transportID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_BluetoothHIDComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_RouteGuidanceDisplayComponent
    if (identification->routeGuidance.id > 0) {
        subpoint = 0;

        uint16 routeGuidanceID = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.id);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_Identifier, (uint8 *)&routeGuidanceID, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_RGDC_Name, (uint8 *)identification->routeGuidance.name.c_str(), identification->routeGuidance.name.length() + 1, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 maxCurRoadNameLen = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxCurRoadNameLen);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxCurrentRoadNameLength, (uint8 *)&maxCurRoadNameLen, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 maxDestNameLen = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxDestNameLen);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxDestinationNameLength, (uint8 *)&maxDestNameLen, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 maxAManeRoadNameLen = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxAManeRoadNameLen);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxAfterManeuverRoadNameLength, (uint8 *)&maxAManeRoadNameLen, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 maxManeDesLen = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxManeDesLen);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxManeuverDescriptionLength, (uint8 *)&maxManeDesLen, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        uint16 maxGuiManeStoCap = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxGuiManeStoCap);
        paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxGuidanceManeuverStorageCapacity, (uint8 *)&maxGuiManeStoCap, 2, subdata);
        memcpy(tempdata + subpoint, subdata, paramLen);
        subpoint += paramLen;

        // uint16 maxLaneGuiDesLen = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxLaneGuiDesLen);
        // paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxLaneGuidanceDescriptionLength, (uint8 *)&maxLaneGuiDesLen, 2, subdata);
        // memcpy(tempdata + subpoint, subdata, paramLen);
        // subpoint += paramLen;

        // uint16 maxLaneGuiStoCap = IAP2_BIG_ENDIAN_SWITCH(identification->routeGuidance.maxLaneGuiStoCap);
        // paramLen = createParamPacket(IAP2_AI_II_RGDC_MaxLaneGuidanceStorageCapacity, (uint8 *)&maxLaneGuiStoCap, 2, subdata);
        // memcpy(tempdata + subpoint, subdata, paramLen);
        // subpoint += paramLen;

        paramLen = createParamPacket(IAP2_AI_II_RouteGuidanceDisplayComponent, (uint8 *)tempdata, subpoint, data);
        memcpy(paramPacket + point,data, paramLen);
        point += paramLen;
    }

    // IAP2_AI_II_ProductPlanUID
    if(m_PrductPlanUIDEnabled){
        paramLen = createParamPacket(IAP2_AI_II_ProductPlanUID, (uint8 *)identification->productPlanUID.c_str(), identification->productPlanUID.length() + 1, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    packetSize = createPayloadPacket(IAP2_ACCESSORY_IDENTIFICATION, IAP2_AI_IdentificationInformation, paramPacket, point, (uint8 *) payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::identificationAccepted() {
    CIpodLog::i("[  Device   ] step 8:  ACK IdentificationAccepted\n");

    m_IdentificationState = IAP2IdentificationState::ACCEPTED;
    return 0;
}

int CIpodIap2::identificationRejected(const uint8 *data)
{
    CIpodLog::i("[  Device   ] step 8:  ACK IdentificationRejected.\n");

    bool needReidentification = false;

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen)
    {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
            case IAP2_AI_IR_Name:
                break;
            case IAP2_AI_IR_ModelIdentifier:
                break;
            case IAP2_AI_IR_Manufacturer:
                break;
            case IAP2_AI_IR_SerialNumber:
                break;
            case IAP2_AI_IR_FirmwareVersion:
                break;
            case IAP2_AI_IR_HardwareVersion:
                break;
            case IAP2_AI_IR_MessagesSentByAccessory:
                if (paramLen > 4) {
                    needReidentification = true;
                    for (int i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i += 2)
                    {
                        uint16 msgID = IAP2_BIG_ENDIAN(data[i + 1 + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH], data[i + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH]);
                        CIpodLog::w("[  Device   ] step 8:  ACK IdentificationRejected MessagesSentByAccessory    MessageID: %04X.\n", msgID );
                        for (std::list<uint16>::iterator iter = m_MessagesSent.begin(); iter != m_MessagesSent.end(); ) {
                            if (msgID == *iter) {
                                m_MessagesSent.erase(iter);
                                break;
                            } else {
                                ++iter;
                            }
                        }
                    }
                }
                break;
            case IAP2_AI_IR_MessagesReceivedFromDevice:
                if (paramLen > 4)
                {
                    needReidentification = true;
                    for (int i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i += 2)
                    {
                        uint16 msgID = IAP2_BIG_ENDIAN(data[i + 1 + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH], data[i + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH]);
                        CIpodLog::w("[  Device   ] step 8:  ACK IdentificationRejected MessagesReceivedFromDevice MessageID: %04X.\n", msgID );
                        for (std::list<uint16>::iterator iter = m_MessagesReceived.begin(); iter != m_MessagesReceived.end(); ) {
                            if (msgID == *iter) {
                                m_MessagesReceived.erase(iter);
                                break;
                            } else {
                                ++iter;
                            }
                        }
                    }
                }
                break;
            case IAP2_AI_IR_PowerProvidingCapability:
                break;
            case IAP2_AI_IR_MaximumCurrentDrawnFromDevice:
                break;
            case IAP2_AI_IR_SupportedExternalAccessoryProtocol:
                break;
            case IAP2_AI_IR_AppMatchTeamID:
                break;
            case IAP2_AI_IR_CurrentLanguage:
                break;
            case IAP2_AI_IR_SupportedLanguage:
                break;
            case IAP2_AI_IR_SerialTransportComponent:
                break;
            case IAP2_AI_IR_USBDeviceTransportComponent:
                break;
            case IAP2_AI_IR_USBHostTransportComponent:
                break;
            case IAP2_AI_IR_BluetoothTransportComponent:
                break;
            case IAP2_AI_IR_iAP2HIDComponent:
                break;
            case IAP2_AI_IR_VehicleInformationComponent:
                break;
            case IAP2_AI_IR_VehicleStatusComponent:
                break;
            case IAP2_AI_IR_LocationInformationComponent:
                break;
            case IAP2_AI_IR_USBHostHIDComponent:
                break;
            case IAP2_AI_IR_WirelessCarPlayTransportComponent:
                break;
            case IAP2_AI_IR_BluetoothHIDComponent:
                break;
            case IAP2_AI_IR_RouteGuidanceDisplayComponent:
                break;
            case IAP2_AI_IR_ProductPlanUID:
                CIpodLog::e("[  Device   ] step 8:  ACK IdentificationRejected ProductPlanUID.\n" );
                m_PrductPlanUIDEnabled = false;
                needReidentification = true;
                break;
            }
        parsedLen += paramLen;
    }

    int ret = -1;
    if (needReidentification) {
        ret = identificationInformation();
    } else {
        m_IdentificationState = IAP2IdentificationState::REJECTED;
        ret = cancelIdentification();
    }
    return ret;
}

int CIpodIap2::cancelIdentification() {
    CIpodLog::i("CIpodIap2::cancelIdentification\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    packetSize = createPayloadPacket(IAP2_ACCESSORY_IDENTIFICATION, IAP2_AI_CancelIdentification, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::identificationInformationUpdate() {
    CIpodLog::i("CIpodIap2::identificationInformationUpdate\n");
    return 0;
}

int CIpodIap2::requestAppLaunch(std::string appBundleID) {
    CIpodLog::i("CIpodIap2::requestAppLaunch, appBundleID = %s\n", appBundleID.c_str());

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_AL_RAL_AppBundleID, (uint8 *) appBundleID.c_str(), appBundleID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_APP_LAUNCH, IAP2_AL_RequestAppLaunch, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::requestAppLaunch(std::string appBundleID, int method) {
    CIpodLog::i("CIpodIap2::requestAppLaunch, appBundleID = %s, method = %d\n", appBundleID.c_str(), method);

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_AL_RAL_AppBundleID, (uint8 *) appBundleID.c_str(), appBundleID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    int8 tempMethod = (int8)(method & 0xff);
    paramLen = createParamPacket(IAP2_AL_RAL_AppLaunchMethod, (uint8 *) &tempMethod, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_APP_LAUNCH, IAP2_AL_RequestAppLaunch, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startAssistiveTouch() {
    CIpodLog::i("CIpodIap2::startAssistiveTouch\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_ASSISTIVE_TOUCH, IAP2_AT_StartAssistiveTouch, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::stopAssistiveTouch() {
    CIpodLog::i("CIpodIap2::stopAssistiveTouch\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_ASSISTIVE_TOUCH, IAP2_AT_StopAssistiveTouch, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startAssistiveTouchInformation() {
    CIpodLog::i("CIpodIap2::startAssistiveTouchInformation\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_ASSISTIVE_TOUCH, IAP2_AT_StartAssistiveTouchInformation, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::assistiveTouchInformation(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_AT_ATI_IsEnabled:
            m_TouchEnabled = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += paramLen;
    }
    CIpodLog::i("CIpodIap2::assistiveTouchInformation, m_TouchEnabled = %d\n", m_TouchEnabled);
    return 0;
}

int CIpodIap2::stopAssistiveTouchInformation() {
    CIpodLog::i("CIpodIap2::stopAssistiveTouchInformation\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_ASSISTIVE_TOUCH, IAP2_AT_StopAssistiveTouchInformation, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::bluetoothComponentInformation(bool connected) {
    CIpodLog::i("CIpodIap2::bluetoothComponentInformation, connected = %d\n", connected);

    uint8 data[1024] = { 0 };
    uint8 subdata[1024] = { 0 };
    uint8 tempdata[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    uint16 point = 0;
    uint16 subpoint = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;

    uint16 bluetoothID = IAP2_BIG_ENDIAN_SWITCH(CIpodConfig::getIdentification()->bluetoothTransport.id);
    paramLen = createParamPacket(IAP2_BC_BCI_BCS_ComponentIdentifier, (uint8 *)&bluetoothID, 2, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_BC_BCI_BCS_ComponentEnabled, (uint8 *)&connected, 1, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_BC_BCI_BluetoothComponentStatus, (uint8 *)tempdata, subpoint, data);
    memcpy(paramPacket + point,data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_BLUETOOTH_CONNECTION, IAP2_BC_BluetoothComponentInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startBluetoothConnectionUpdates() {
    CIpodLog::i("CIpodIap2::startBluetoothConnectionUpdates\n");

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 bluetoothID = IAP2_BIG_ENDIAN_SWITCH(CIpodConfig::getIdentification()->bluetoothTransport.id);
    packetSize = createParamPacket(IAP2_BC_SBCU_BluetoothTransportComponentIdentifier, (uint8 *)&bluetoothID, 2, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_BLUETOOTH_CONNECTION, IAP2_BC_StartBluetoothConnectionUpdates, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::bluetoothConnectionUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_BC_BCU_BluetoothTransportComponentIdentifier:
            break;
        case IAP2_BC_BCU_ConnectedBluetoothProfiles: {
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            parseConnectedBluetoothProfiles(paramBuf, paramLen);
            free(paramBuf);
            break;
        }
        }
        parsedLen += paramLen;
    }
    return 0;
}

int CIpodIap2::stopBluetoothConnectionUpdates() {
    CIpodLog::i("CIpodIap2::stopBluetoothConnectionUpdates\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_BLUETOOTH_CONNECTION, IAP2_BC_StopBluetoothConnectionUpdates, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

void CIpodIap2::parseConnectedBluetoothProfiles(const uint8 *data, uint16 len) {
    uint16 subParsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;
    bool a2dpConnected = false;

    subParsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (subParsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(subParsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(subParsedLen + 2);
        switch (subParamID) {
        case IAP2_BC_BCU_BCP_BluetoothHandsFree:
            break;
        case IAP2_BC_BCU_BCP_BluetoothPhoneBookAccess:
            break;
        case IAP2_BC_BCU_BCP_BluetoothAudioVideoRemoteControl:
            break;
        case IAP2_BC_BCU_BCP_BluetoothAdvancedAudioDistribution:
            a2dpConnected = true;
            break;
        case IAP2_BC_BCU_BCP_BluetoothHumanInterfaceDevice:
            break;
        case IAP2_BC_BCU_BCP_BluetoothiAP2Link:
            break;
        case IAP2_BC_BCU_BCP_BluetoothPersonalAreaNetworkAccessPoint:
            break;
        case IAP2_BC_BCU_BCP_BluetoothMessageAccess:
            break;
        case IAP2_BC_BCU_BCP_BluetoothPersonalAreaNetworkClient:
            break;
        }
        subParsedLen += subParamLen;
    }

    if (a2dpConnected != m_A2dpConnected) {
        m_A2dpConnected = a2dpConnected;
        CIpodEvent::sendEvent(IPOD_EVT_A2DP_STATUS_CHANGED, getHandle(), m_A2dpConnected);
    }
}

int CIpodIap2::startCallStateUpdates() {
    CIpodLog::w("Accessory StartCallStateUpdates\n");

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_C_SCSU_RemoteID, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_DisplayName, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_Status, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_Direction, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_CallUUID, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_AddressBookID, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_Label, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_Service, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_IsConferenced, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_ConferenceGroup, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_C_SCSU_DisconnectReason, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_COMMUNICATIONS, IAP2_C_StartCallStateUpdates, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::callStateUpdate(const uint8 *data) {
    CIpodLog::w("Device CallStateUpdate\n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_C_CSU_RemoteID:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memset( buf, '\0', paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH );
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_CallStatus.remoteID = buf;
            }
            break;
        case IAP2_C_CSU_DisplayName:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memset( buf, '\0', paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH );
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_CallStatus.displayName = buf;
            }
            break;
        case IAP2_C_CSU_Status:
            m_CallStatus.status = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_C_CSU_Direction:
            m_CallStatus.direction = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_C_CSU_CallUUID:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memset( buf, '\0', paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH );
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_CallStatus.callUUID = buf;
            }
            break;
        case IAP2_C_CSU_AddressBookID:
            break;
        case IAP2_C_CSU_Label:
            break;
        case IAP2_C_CSU_Service:
            break;
        case IAP2_C_CSU_IsConferenced:
            break;
        case IAP2_C_CSU_ConferenceGroup:
            break;
        case IAP2_C_CSU_DisconnectReason:
            break;
        }
        parsedLen += paramLen;
    }

    CIpodEvent::sendEvent(IPOD_EVT_CALL_STATUS_CHANGED, getHandle(),&m_CallStatus );

    return 0;
}

int CIpodIap2::stopCallStateUpdates() {
    CIpodLog::i("CIpodIap2::stopCallStateUpdates\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_COMMUNICATIONS, IAP2_C_StopCallStateUpdates, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startCommunicationsUpdates() {
    CIpodLog::i("CIpodIap2::startCommunicationsUpdates, not support!\n");
    return -1;
}

int CIpodIap2::communicationsUpdate(const uint8 *data) {
    CIpodLog::i("CIpodIap2::communicationsUpdate, not support!\n");
    return -1;
}

int CIpodIap2::stopCommunicationsUpdates() {
    CIpodLog::i("CIpodIap2::stopCommunicationsUpdates, not support!\n");
    return -1;
}

int CIpodIap2::initiateCall() {
    CIpodLog::i("CIpodIap2::initiateCall, not support!\n");
    return -1;
}

int CIpodIap2::acceptCall() {
    CIpodLog::i("CIpodIap2::acceptCall, not support!\n");
    return -1;
}

int CIpodIap2::endCall() {
    CIpodLog::i("CIpodIap2::endCall, not support!\n");
    return -1;
}

int CIpodIap2::swapCall() {
    CIpodLog::i("CIpodIap2::swapCall, not support!\n");
    return -1;
}

int CIpodIap2::mergeCall() {
    CIpodLog::i("CIpodIap2::mergeCall, not support!\n");
    return -1;
}

int CIpodIap2::holdStatusUpdate() {
    CIpodLog::i("CIpodIap2::holdStatusUpdate, not support!\n");
    return -1;
}

int CIpodIap2::muteStatusUpdate() {
    CIpodLog::i("CIpodIap2::muteStatusUpdate, not support!\n");
    return -1;
}

int CIpodIap2::sendDTMF() {
    CIpodLog::i("CIpodIap2::sendDTMF, not support!\n");
    return -1;
}

int CIpodIap2::startListUpdates() {
    CIpodLog::i("CIpodIap2::startListUpdates, not support!\n");
    return -1;
}

int CIpodIap2::listUpdate(const uint8 *data) {
    CIpodLog::i("CIpodIap2::listUpdate, not support!\n");
    return -1;
}

int CIpodIap2::stopListUpdates() {
    CIpodLog::i("CIpodIap2::stopListUpdates, not support!\n");
    return -1;
}

int CIpodIap2::requestDeviceAuthenticationCertificate() {
    CIpodLog::i("CIpodIap2::requestDeviceAuthenticationCertificate, not support!\n");
    return -1;
}

int CIpodIap2::deviceAuthenticationCertificate(const uint8 *data) {
    CIpodLog::i("CIpodIap2::deviceAuthenticationCertificate, not support!\n");
    return -1;
}

int CIpodIap2::RequestDeviceAuthenticationChallengeResponse(const uint8 *challenge, uint16 len) {
    CIpodLog::i("CIpodIap2::RequestDeviceAuthenticationChallengeResponse, not support!\n");
    return -1;
}

int CIpodIap2::deviceAuthenticationResponse(const uint8 *data) {
    CIpodLog::i("CIpodIap2::deviceAuthenticationResponse, not support!\n");
    return -1;
}

int CIpodIap2::deviceAuthenticationFailed() {
    CIpodLog::i("CIpodIap2::deviceAuthenticationFailed, not support!\n");
    return -1;
}

int CIpodIap2::deviceAuthenticationSucceeded() {
    CIpodLog::i("CIpodIap2::deviceAuthenticationSucceeded, not support!\n");
    return -1;
}

int CIpodIap2::deviceInformationUpdate(const uint8 *data) {

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

//    std::string deviceName;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_DIU_DeviceName:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_DeviceInformation = buf;
            }
            break;
        }
        parsedLen += paramLen;
    }

    CIpodLog::i("[  Device   ] DeviceInformationUpdate: %s.\n", m_DeviceInformation.c_str());
    CIpodEvent::sendEvent(IPOD_EVT_DEVICE_INFORMATION_UPDATE, getHandle(), m_DeviceInformation.c_str());

    return 0;
}

int CIpodIap2::getDeviceInformation(){
    CIpodLog::i("CIpodIap2::getDeviceInformation, deviceName = %s\n", m_DeviceInformation.c_str());
    CIpodEvent::sendEvent(IPOD_EVT_DEVICE_INFORMATION_UPDATE, getHandle(), m_DeviceInformation.data());
	return 0;
}

int CIpodIap2::deviceLanguageUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_DLU_DeviceLanguage:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_DeviceLanguage = buf;
            }
            break;
        }
        parsedLen += paramLen;
    }
    CIpodLog::i("[  Device   ] DeviceLanguageUpdate: %s.\n", m_DeviceLanguage.c_str());
    return 0;
}

int CIpodIap2::deviceTimeUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    uint64 seconds = 0;
    int16 timezone = 0;
    int8 daylight = 0;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_DTU_SecondsSinceReferenceDate: {
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                seconds = seconds * 256 + data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH + i];
            }
            break;
        }
        case IAP2_DN_DTU_TimeZoneOffsetMinutes: {
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                timezone = timezone * 256 + data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH + i];
            }
            break;
        }
        case IAP2_DN_DTU_DaylightSavingsOffsetMinutes:
            daylight = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += paramLen;
    }
    CIpodLog::i("CIpodIap2::deviceUUIDUpdate, seconds = %llu, timezone = %d, daylight = %d\n", seconds, timezone, daylight);
    return 0;
}

int CIpodIap2::deviceUUIDUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    std::string uuid;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_DUUIDU_UUID:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                uuid = buf;
            }
            break;
        }
        parsedLen += paramLen;
    }
    CIpodLog::i("CIpodIap2::deviceUUIDUpdate, uuid = %s\n", uuid.c_str());
    return 0;
}

int CIpodIap2::wirelessCarPlayUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    m_WirelessAvailable = IAP2WirelessCarPlayStatus::UNAVAILABLE;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_WCU_Status:
            m_WirelessAvailable = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += paramLen;
    }
    CIpodLog::i("[  Device   ] WirelessCarPlayUpdate: %d\n", m_WirelessAvailable);
    CIpodEvent::sendEvent(IPOD_EVT_WIRELESS_CARPLAY_AVAILABLE_CHANGED, getHandle(), m_WirelessAvailable);
    return 0;
}

int CIpodIap2::deviceTransportIdentifierNotification(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_DN_DTIN_BluetoothTransportIdentifier:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_DeviceTransportId.BluetoothTransportIdentifier = buf;
            }
            break;
        case IAP2_DN_DTIN_USBTransportIdentifier:
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                m_DeviceTransportId.USBTransportIdentifier = buf;
            }
            break;
        }
        parsedLen += paramLen;
    }

    CIpodLog::d("Device DeviceTransportIdentifierNotification BluetoothTransportIdentifier: %s, USBTransportIdentifier: %s\n",
        m_DeviceTransportId.BluetoothTransportIdentifier.c_str(), m_DeviceTransportId.USBTransportIdentifier.c_str());
    CIpodEvent::sendEvent(IPOD_EVT_DEVICE_TRANSPORTID_CHANGED, getHandle(), &m_DeviceTransportId);

    return 0;
}

int CIpodIap2::startExternalAccessoryProtocol(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    uint8 eapid = 0;
    uint16 eapSessionID = 0;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_EAP_StrEAPS_ExternalAccessoryProtocolIdentifier:
            eapid = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_EAP_StrEAPS_ExternalAccessoryProtocolSessionIdentifier:
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                eapSessionID = eapSessionID * 256 + data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH + i];
            }
            break;
        }
        parsedLen += paramLen;
    }

    m_EAPStatus.id = eapid;
    m_EAPStatus.sessionID = eapSessionID;
    m_EAPStatus.status = IAP2EAPSessionStatus::OK;
    m_EAPStatus.protocol = CIpodConfig::getIdentification()->eaprotocol.name;
    CIpodLog::i("CIpodIap2::startExternalAccessoryProtocol, id = %u, sessionid = %u, status = %d, protocol = %s\n",
            m_EAPStatus.id, m_EAPStatus.sessionID, m_EAPStatus.status, m_EAPStatus.protocol.c_str());
    return 0;
}

int CIpodIap2::stopExternalAccessoryProtocol(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    uint16 eapSessionID = 0;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_EAP_StpEAPS_ExternalAccessoryProtocolSessionIdentifier:
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                eapSessionID = eapSessionID * 256 + data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH + i];
            }
            break;
        }
        parsedLen += paramLen;
    }
    m_EAPStatus.id = 0;
    m_EAPStatus.sessionID = 0;
    m_EAPStatus.status = IAP2EAPSessionStatus::CLOSED;
    m_EAPStatus.protocol.clear();
    CIpodLog::i("CIpodIap2::stopExternalAccessoryProtocol, eapSessionID = %u\n", eapSessionID);
    return 0;
}

int CIpodIap2::statusExternalAccessoryProtocol(uint16 sessionID, int status) {
    CIpodLog::i("CIpodIap2::statusExternalAccessoryProtocol, sessionID = %u, status = %d\n", sessionID, status);

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 tempEAPSessionID = IAP2_BIG_ENDIAN_SWITCH(sessionID);
    paramLen = createParamPacket(IAP2_EAP_StuEAPS_ExternalAccessoryProtocolSessionIdentifier, (uint8 *) &tempEAPSessionID, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_EAP_StuEAPS_ExternalAccessoryProtocolSessionStatus, (uint8 *) &status, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_EXTERNAL_ACCESSORY_PROTOCOL, IAP2_EAP_StatusExternalAccessoryProtocolSession, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startHID(uint16 id, uint16 vid, uint16 pid, const uint8 *descriptor, uint16 descriptorSize) {
    CIpodLog::i("CIpodIap2::startHID, id = %u, vid = %u, pid = %u\n", id, vid, pid);

    uint16 point = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 tempID = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    tempID = IAP2_BIG_ENDIAN_SWITCH(id);
    paramLen = createParamPacket(IAP2_HID_StrHID_HIDComponentIdentifier, (uint8*)&tempID, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    tempID = IAP2_BIG_ENDIAN_SWITCH(vid);
    paramLen = createParamPacket(IAP2_HID_StrHID_VendorIdentifier, (uint8*)&tempID, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    tempID = IAP2_BIG_ENDIAN_SWITCH(pid);
    paramLen = createParamPacket(IAP2_HID_StrHID_ProductIdentifier, (uint8*)&tempID, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_HID_StrHID_HIDReportDescriptor, descriptor, descriptorSize, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_HID, IAP2_HID_StartHID, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::deviceHIDReport(const uint8 *data) {
    CIpodLog::i("CIpodIap2::deviceHIDReport, not support\n");
    return -1;
}

int CIpodIap2::accessoryHIDReport(uint16 id, const uint8 *report, uint16 reportSize) {
    CIpodLog::i("CIpodIap2::accessoryHIDReport, id = %u\n", id);

    uint16 point = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 tempID = IAP2_BIG_ENDIAN_SWITCH(id);
    paramLen = createParamPacket(IAP2_HID_AHIDR_HIDComponentIdentifier, (uint8 *)&tempID, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_HID_AHIDR_HIDReport, report, reportSize, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_HID, IAP2_HID_AccessoryHIDReport, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::stopHID(uint16 id) {
    CIpodLog::i("CIpodIap2::stopHID, id = %u\n", id);

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 tempID = IAP2_BIG_ENDIAN_SWITCH(id);
    packetSize = createParamPacket(IAP2_HID_StpHID_HIDComponentIdentifier, (uint8 *)&tempID, 2, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_HID, IAP2_HID_StopHID, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startNativeHID() {
    CIpodLog::i("CIpodIap2::startNativeHID, not support\n");
    return -1;
}

int CIpodIap2::startLocationInformation(const uint8 *data) {
    CIpodLog::w("[  Device   ] StartLocationInformation\n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    m_LocationType.value = 0;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_L_SLI_GlobalPositioningSystemFixData:
            m_LocationType.GlobalPositioningSystemFixData = 1;
            break;
        case IAP2_L_SLI_RecommendedMinimumSpecificGPSTransitData:
            m_LocationType.RecomendedSpecificGPSTransitData = 1;
            break;
        case IAP2_L_SLI_GPSSatellitesInView:
            m_LocationType.GPSSatellitesInView = 1;
            break;
        case IAP2_L_SLI_VehicleSpeedData:
            m_LocationType.VehicleSpeedData = 1;
            break;
        case IAP2_L_SLI_VehicleGyroData:
            m_LocationType.VehicleGyroData = 1;
            break;
        case IAP2_L_SLI_VehicleAccelerometerData:
            m_LocationType.VehicleAccelerometerData = 1;
            break;
        case IAP2_L_SLI_VehicleHeadingData:
            m_LocationType.VehicleHeadingData = 1;
            break;
        }
        parsedLen += paramLen;
    }

    CIpodEvent::sendEvent(IPOD_EVT_LOCATION_ENABLE_CHANGED, getHandle(),1,&m_LocationType );

    return 0;
}

int CIpodIap2::GPRMCDDataStatusValuesNotification(const uint8 *data) {
    CIpodLog::i("CIpodIap2::GPRMCDDataStatusValuesNotification, not support\n");
    return -1;
}

int CIpodIap2::locationInformation(std::string nmeaSentences) {
    CIpodLog::d("Accessory NMEA Sentences: %s\n", nmeaSentences.c_str());

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_L_LI_NMEASentence, (uint8 *) nmeaSentences.c_str(), nmeaSentences.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_LOCATION, IAP2_L_LocationInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::stopLocationInformation() {
    CIpodLog::w("[  Device   ] StopLocationInformation\n");

    CIpodEvent::sendEvent(IPOD_EVT_LOCATION_ENABLE_CHANGED, getHandle(),0);

    return 0;
}

int CIpodIap2::initMedia() {
    return startMediaLibraryInformation();
}

int CIpodIap2::deinitMedia() {
    if (!m_LocalDeviceLibrary.id.empty()) {
        stopMediaLibraryUpdates(m_LocalDeviceLibrary.id);
        m_LocalDeviceSqlite.close();
    }
    if (!m_MusicRadioLibrary.id.empty()) {
        stopMediaLibraryUpdates(m_MusicRadioLibrary.id);
        m_MusicRadioSqlite.close();
    }
    return 0;
}

int CIpodIap2::startMediaLibraryInformation() {
    CIpodLog::w("Accessory StartMediaLibraryInformation\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_StartMediaLibraryInformation, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::mediaLibraryInformation(const uint8 *data) {
    CIpodLog::i("[  Device   ] MediaLibraryInformation\n");

    int ret = -1;
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_MLA_MLI_MediaLibraryInformation: {
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            parseMediaLibraryInformation(paramBuf, paramLen);
            free(paramBuf);
            break;
        }
        }
        parsedLen += paramLen;
    }

    CIpodLog::i("[  Device   ] MediaLibraryType: %d, MediaLibraryName: %s, MediaLibraryUniqueIdentifier: %s\n", 
        m_LocalDeviceLibrary.type, m_LocalDeviceLibrary.name.c_str(), m_LocalDeviceLibrary.id.c_str() );
    CIpodLog::i("[  Device   ] MediaLibraryType: %d, MediaLibraryName: %s, MediaLibraryUniqueIdentifier: %s\n", 
        m_MusicRadioLibrary.type, m_MusicRadioLibrary.name.c_str(), m_MusicRadioLibrary.id.c_str());
  	
	if (!m_LocalDeviceLibrary.id.empty()) {
    	ret = stopMediaLibraryInformation();
    	if (ret == 0) {
            char localDevicePath[1024];
            sprintf(localDevicePath, "%s/%s", CIpodVehicle::getCachePath(), m_LocalDeviceLibrary.id.c_str());
            if (access(localDevicePath, 0) != 0) {
                char buf[128];
                sprintf(buf, "mkdir -p %s", localDevicePath);
                system(buf);
            }
            CIpodLog::i( "sqlite3_open filename: %s\n", localDevicePath );
            ret = m_LocalDeviceSqlite.open(localDevicePath);
            if (ret == 0) {
                startMediaLibraryUpdates(m_LocalDeviceLibrary.id);
            }
            CIpodEvent::sendEvent(IPOD_EVT_FILE_MUSIC_LOCAL_DEVICE_PATH, getHandle(), localDevicePath);
        }
	}

	if (!m_MusicRadioLibrary.id.empty()) {
		char musicRadioPath[1024];
		sprintf(musicRadioPath, "%s/%s", CIpodVehicle::getCachePath(), m_MusicRadioLibrary.id.c_str());
		if (access(musicRadioPath, 0) != 0) {
			char buf[128];
			sprintf(buf, "mkdir -p %s", musicRadioPath);
			system(buf);
		}
        CIpodLog::i( "sqlite3_open filename: %s\n", musicRadioPath );
		ret = m_MusicRadioSqlite.open(musicRadioPath);
		if (ret == 0) {
			startMediaLibraryUpdates(m_MusicRadioLibrary.id);
		}
        CIpodEvent::sendEvent(IPOD_EVT_FILE_MUSIC_RADIO_PATH, getHandle(), musicRadioPath);
	}
    return ret;
}

int CIpodIap2::stopMediaLibraryInformation() {
    CIpodLog::i("[ Accessory ] StopMediaLibraryInformation\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_StopMediaLibraryInformation, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startMediaLibraryUpdates(std::string libraryID) {
    CIpodLog::w("[ Accessory ] StartMediaLibraryUpdates, MediaLibraryUniqueIdentifier: %s\n", libraryID.c_str() );

    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint16 point = 0;
    uint16 subpoint = 0;
    uint8 data[1024] = { 0 };
    uint8 subdata[1024] = { 0 };
    uint8 tempdata[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    /* MediaLibraryUniqueIdentifier start */
    paramLen = createParamPacket(IAP2_MLA_SMLU_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* MediaLibraryUniqueIdentifier end */

    /* LastKnownMediaLibraryRevision start */
    char path[1024] = { 0 };
    sprintf(path, "%s/%s/%s", CIpodVehicle::getCachePath(), libraryID.c_str(), LIBRARY_REVISION);
    FILE *file = fopen(path, "r");
    if (file != NULL) {
        fseek(file, 0, SEEK_END);
        int size = ftell(file);
        char *libraryRevision;
        libraryRevision = (char *)malloc(size + 1);
        rewind(file);
        fread(libraryRevision, sizeof(char), size, file);
        fclose(file);
        libraryRevision[size] = '\0';

        if (strcmp(libraryRevision, "") == 0) {
            CIpodLog::i("CIpodIap2::startMediaLibraryUpdates, library_version is empty!\n");
        } else {
            CIpodLog::i("[ Accessory ] StartMediaLibraryUpdates, LastKnownMediaLibraryRevision: %s\n", libraryRevision);
            paramLen = createParamPacket(IAP2_MLA_SMLU_LastKnownMediaLibraryRevision, (uint8 *)libraryRevision, strlen(libraryRevision) + 1, data);
            memcpy(paramPacket+point,data,paramLen);
            point += paramLen;
        }
        free(libraryRevision);
    }
    /* LastKnownMediaLibraryRevision end */

    /* MediaItemProperties start */
    subpoint = 0;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyPersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyTitle, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyMediaType, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyRating, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyPlaybackDurationInMilliseconds, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumPersistentIdentifer, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumTitle, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumTrackNumber, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumTrackCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumDiscNumber, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumDiscCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyArtistPersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyArtist, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumArtistPersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyAlbumArtist, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyGenrePersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyGenre, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyComposerPersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyComposer, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyIsPartOfCompilation, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyIsResidentOnDevice, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MIP_MediaItemPropertyChapterCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SMLU_MediaItemProperties, (uint8 *)tempdata, subpoint, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* MediaItemProperties end */

    /* MediaPlaylistProperties start */
    subpoint = 0;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyPersistentIdentifer, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyName, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyParentPersistentIdentifer, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyIsGeniusMix, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyIsFolder, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyContainedMediaItemsFileTransferIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SrMLU_MPP_MediaPlaylistPropertyIsAppleMusicRadioStation, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_MLA_SMLU_MediaPlaylistProperties, (uint8 *)tempdata, subpoint, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* MediaPlaylistProperties end */

    /* MediaLibraryUpdateProgress start */
    paramLen = createParamPacket(IAP2_MLA_SMLU_MediaLibraryUpdateProgress, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* MediaLibraryUpdateProgress end */

    /* IAP2_ML_SrMLU_MediaLibraryIsHidingRemoteItems start */
    paramLen = createParamPacket(IAP2_MLA_SMLU_MediaLibraryIsHidingRemoteItems, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* IAP2_ML_SrMLU_MediaLibraryIsHidingRemoteItems end */

    /* PlayAllSongsCapable start */
    paramLen = createParamPacket(IAP2_MLA_SMLU_PlayAllSongsCapable, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* PlayAllSongsCapable end */

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_StartMediaLibraryUpdates, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::mediaLibraryUpdate(const uint8 *data) {
//	CIpodLog::i("CIpodIap2::mediaLibraryUpdate, --- in --- \n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    IAP2MediaLibrary library;
    std::list<IAP2MediaItem> items;
    std::list<IAP2MediaPlaylist> playlists;
    std::list<uint64> itemDeletedIDs;
    std::list<uint64> playlistDeletedIDs;
    bool resetLibrary = false;
    bool hasHideRemoteItems = false;
    bool hasPlayAllCapable = false;
    bool hasUpdatePlaylistName = false;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
//		CIpodLog::i("CIpodIap2::mediaLibraryUpdate, --- paramID == %d \n", paramID);
        switch (paramID) {
        case IAP2_MLA_MLU_MediaLibraryUniqueIdentifier: {
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                library.id = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MediaLibraryRevision: {
            if (paramLen > IAP2_MESSAGE_PARAMETER_HEADER_LENGTH) {
                char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
                library.revision = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MediaItem: {
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            IAP2MediaItem item;
            parseMediaLibraryItem(paramBuf, paramLen, &item);
            items.push_back(item);
            free(paramBuf);
            break;
        }
        case IAP2_MLA_MLU_MediaPlaylist: {
//			CIpodLog::i("CIpodIap2::mediaLibraryUpdate, --- IAP2_MLA_MLU_MediaPlaylist --- \n");
			hasUpdatePlaylistName = true;
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            IAP2MediaPlaylist playlist;
            parseMediaLibraryPlaylist(paramBuf, paramLen, &playlist);
            playlists.push_back(playlist);
            free(paramBuf);
            break;
        }
        case IAP2_MLA_MLU_MediaItemDeletePersistentIdentifier: {
//			CIpodLog::i("CIpodIap2::mediaLibraryUpdate, ---Delete Item --- \n");
            uint64 id = 0;
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            itemDeletedIDs.push_back(id);
            break;
        }
        case IAP2_MLA_MLU_MediaPlaylistDeletePersistentIdentifier: {
//			CIpodLog::i("CIpodIap2::mediaLibraryUpdate, ---Delete List --- \n");
            uint64 id = 0;
            for (int32 i = 0; i < paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            playlistDeletedIDs.push_back(id);
            break;
        }
        case IAP2_MLA_MLU_MediaLibraryReset: {
            resetLibrary = true;
            break;
        }
        case IAP2_MLA_MLU_MediaLibraryUpdateProgress: {
            library.progress = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MediaLibraryIsHidingRemoteItems: {
//            hasHideRemoteItems = true;
            library.hideRemoteItems = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_PlayAllSongsCapable: {
            hasPlayAllCapable = true;
            library.allSongsCapable = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        }
        parsedLen += paramLen;
    }

    CIap2Sqlite *sqlite = NULL;
    if (library.id.compare(m_LocalDeviceLibrary.id) == 0) {
        library.type = IAP2MediaLibraryType::LOCAL_DEVICE;
        sqlite = &m_LocalDeviceSqlite;
        bool needNotifyUpdate = true;
        if (m_LocalDeviceLibrary.progress == 100) {
            if (itemDeletedIDs.empty() && playlistDeletedIDs.empty() && playlists.empty() && !hasHideRemoteItems && !resetLibrary) {
                bool itemChanged = false;
                for (std::list<IAP2MediaItem>::iterator iter = items.begin(); iter != items.end(); ++iter) {
                    if (!sqlite->isItemExist(*iter)) {
                        itemChanged = true;
                        break;
                    }
                }
                if (!itemChanged) {
                    needNotifyUpdate = false;
                }
            }
        }
        m_LocalDeviceLibrary.progress = library.progress;
        if (needNotifyUpdate) {
            CIpodEvent::sendEvent(IPOD_EVT_MEDIA_UPDATE_PROGRESS, getHandle(), m_LocalDeviceLibrary.progress);
        }
        m_LocalDeviceLibrary.revision = library.revision;
        if (hasHideRemoteItems) {
            m_LocalDeviceLibrary.hideRemoteItems = library.hideRemoteItems;
        }
        if (hasPlayAllCapable) {
            m_LocalDeviceLibrary.allSongsCapable = library.allSongsCapable;
        }
    } else if (library.id.compare(m_MusicRadioLibrary.id) == 0) {
        library.type = IAP2MediaLibraryType::MUSIC_RADIO;
        sqlite = &m_MusicRadioSqlite;
        m_MusicRadioLibrary.revision = library.revision;
        m_MusicRadioLibrary.progress = library.progress;
        if (hasHideRemoteItems) {
            m_MusicRadioLibrary.hideRemoteItems = library.hideRemoteItems;
        }
        if (hasPlayAllCapable) {
            m_MusicRadioLibrary.allSongsCapable = library.allSongsCapable;
        }
    } else if (library.id.compare(m_LocalDeviceLibrary.id) == 1) {
		CIpodLog::i("CIpodIap2::mediaLibraryUpdate, ---m_LocalDeviceLibrary.id == library.id --- \n");
	}

    char path[1024];
    sprintf(path, "%s/%s/%s", CIpodVehicle::getCachePath(), library.id.c_str(), LIBRARY_REVISION);
    FILE *file = fopen(path, "w+");
    if (file != NULL) {
        fwrite(library.revision.c_str(), library.revision.length() + 1, 1, file);
        fclose(file);
    }
    if (sqlite != NULL) {
        sqlite->beginTransaction();
        if (resetLibrary) {
            sqlite->reset();
        }
        for (std::list<IAP2MediaItem>::iterator iter = items.begin(); iter != items.end(); ++iter) {
            sqlite->insertMediaItem(*iter);
        }
        CIpodLog::d("CIpodIap2::mediaLibraryUpdate, hasUpdatePlaylistName = %d \n", hasUpdatePlaylistName);
        for (std::list<IAP2MediaPlaylist>::iterator iter = playlists.begin(); iter != playlists.end(); ++iter) {
            if ((*iter).fileTransferID != 0) {
                IAP2FileTransfer *transfer = (IAP2FileTransfer *) malloc(sizeof(IAP2FileTransfer));
                memset(transfer, 0, sizeof(IAP2FileTransfer));
                transfer->id = (*iter).fileTransferID;
                transfer->type = IAP2FileTransferType::PLAYLIST;
                transfer->libraryType = library.type;
                transfer->playlistid = (*iter).id;
                m_FileTransfers.push_back(transfer);
            }
            sqlite->insertMediaPlaylist(*iter);
			if(hasUpdatePlaylistName){
				if(sqlite->isMediaPlaylistExist(*iter)) {
					int updateRet = sqlite->updateMediaPlaylistName(*iter);
					CIpodLog::i("update playlist result: %d\n", updateRet);
				}
				hasUpdatePlaylistName = false;
			}
        }
        for (std::list<uint64>::iterator iter = itemDeletedIDs.begin(); iter != itemDeletedIDs.end(); ++iter) {
            sqlite->deleteMediaItem(*iter);
        }
        for (std::list<uint64>::iterator iter = playlistDeletedIDs.begin(); iter != playlistDeletedIDs.end(); ++iter) {
            sqlite->deleteMediaPlaylist(*iter);
        }
        sqlite->commitTransaction();
    }
    return 0;
}

int CIpodIap2::stopMediaLibraryUpdates(std::string libraryID) {
    CIpodLog::i("CIpodIap2::stopMediaLibraryUpdates, libraryID = %s\n", libraryID.c_str());

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createParamPacket(IAP2_MLA_StpMLU_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_StopMediaLibraryUpdates, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::playMediaLibraryCurrentSelection(std::string libraryID) {
    CIpodLog::i("CIpodIap2::playMediaLibraryCurrentSelection, libraryID = %s\n", libraryID.c_str());

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createParamPacket(IAP2_MLA_PMLCS_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_PlayMediaLibraryCurrentSelection, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::playMediaLibraryItems(std::string libraryID, const uint64 *itemids, uint16 itemSize, uint32 startIndex) {
    CIpodLog::i("CIpodIap2::playMediaLibraryItems, libraryID = %s, itemSize = %lu, startIndex = %lu\n", libraryID.c_str(), itemSize, startIndex);

    uint16 paramLen = 0;
    uint16 point = 0;
    uint16 packetSize = 0;
    uint8 data[65535] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint64 tempid = 0;
    uint8 idsize = sizeof(uint64);
    uint8 itemidArray[itemSize * idsize];
    for (uint32 i = 0; i < itemSize; i++) {
        tempid = itemids[i];
        CIpodLog::i("CIpodIap2::playMediaLibraryItems, tempid = %lu \n", tempid);
        for (int32 j = 7; j >= 0; j--) {
            itemidArray[i * idsize + j] = tempid % 256;
            tempid = tempid / 256;
        }		
    }

    paramLen = createParamPacket(IAP2_MLA_PMLI_ItemsPersistentIdentifiers, (uint8 *) itemidArray, itemSize * idsize, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    uint8 startIndexArray[4];
    startIndexArray[0] = (startIndex >> 24) & 0xFF;
    startIndexArray[1] = (startIndex >> 16)& 0xFF;
    startIndexArray[2] = (startIndex >> 8) & 0xFF;
    startIndexArray[3] = (startIndex) & 0xFF;
    paramLen = createParamPacket(IAP2_MLA_PMLI_ItemsStartingIndex, (uint8 *)startIndexArray, 4, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_MLA_PMLI_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_PlayMediaLibraryItems, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::playMediaLibraryCollection(std::string libraryID, uint64 collectionID, int type, uint32 startIndex) {
    CIpodLog::i("CIpodIap2::playMediaLibraryCollection, libraryID = %s, collectionID = %llu, type = %d, startIndex = %lu\n", libraryID.c_str(), collectionID, type, startIndex);

    uint16 paramLen = 0;
    uint16 point = 0;
    uint16 packetSize = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint8 idArray[sizeof(uint64)];
    for (uint32 i = 0; i < 8; i++) {
        idArray[7 - i] = (collectionID >> (i * 8)) & 0xFF;
    }
    paramLen = createParamPacket(IAP2_MLA_PMLC_CollectionPersistentIdentifier, (uint8 *)idArray, sizeof(uint64), data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    uint8 typeArray[1];
    typeArray[0] = type & 0xFF;
    paramLen = createParamPacket(IAP2_MLA_PMLC_CollectionType, (uint8*)typeArray, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    uint8 startIndexArray[4];
    startIndexArray[0] = (startIndex >> 24) & 0xFF;
    startIndexArray[1] = (startIndex >> 16)& 0xFF;
    startIndexArray[2] = (startIndex >> 8) & 0xFF;
    startIndexArray[3] = (startIndex) & 0xFF;
    paramLen = createParamPacket(IAP2_MLA_PMLC_CollectionStartingIndex, (uint8 *)startIndexArray, 4, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_MLA_PMLC_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_PlayMediaLibraryCollection, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::playMediaLibrarySpecial(std::string libraryID, bool allSongs) {
    CIpodLog::i("CIpodIap2::playMediaLibrarySpecial, libraryID = %s, allSongs = %d\n", libraryID.c_str(), allSongs);

    uint16 paramLen = 0;
    uint16 point = 0;
    uint16 packetSize = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_MLA_PMLS_MediaLibraryUniqueIdentifier, (uint8 *)libraryID.c_str(), libraryID.length() + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    if (allSongs) {
        paramLen = createParamPacket(IAP2_MLA_PMLS_AllSongs, NULL, 0, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    packetSize = createPayloadPacket(IAP2_MEDIA_LIBRARY_ACCESS, IAP2_MLA_PlayMediaLibrarySpecial, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::parseMediaLibraryInformation(const uint8 *data, uint16 len) {

    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;
    IAP2MediaLibrary library;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_MLA_MLI_MLI_MediaLibraryName:
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                library.name = buf;
            }
            break;
        case IAP2_MLA_MLI_MLI_MediaLibraryUniqueIdentifier:
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                library.id = buf;
            }
            break;
        case IAP2_MLA_MLI_MLI_MediaLibraryType:
            library.type = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += subParamLen;
    }

    if (library.type == IAP2MediaLibraryType::LOCAL_DEVICE) {
        m_LocalDeviceLibrary.name = library.name;
        m_LocalDeviceLibrary.id.assign(library.id);
        m_LocalDeviceLibrary.type = library.type;
    } else if (library.type == IAP2MediaLibraryType::MUSIC_RADIO) {
        m_MusicRadioLibrary.name = library.name;
        m_MusicRadioLibrary.id = library.id;
        m_MusicRadioLibrary.type = library.type;
    }

    return 0;
}

int CIpodIap2::parseMediaLibraryItem(const uint8 *data, uint16 len, IAP2MediaItem *item) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_MLA_MLU_MI_MediaItemPersistentIdentifier: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->id = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemTitle: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->title = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemMediaType: {
            item->type = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemRating: {
            item->rating = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemPlaybackDurationInMilliseconds: {
            uint32 duration = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                duration = duration * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->duration = duration;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumPersistentIdentifer: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumID = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumTitle: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->album = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumTrackNumber: {
            uint16 trackNumber;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                trackNumber = trackNumber * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumTrackNumber = trackNumber;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumTrackCount: {
            uint16 trackCount;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                trackCount = trackCount * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumTrackCount = trackCount;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumDiscNumber: {
            uint16 discNumber;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                discNumber = discNumber * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumDiscNumber = discNumber;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumDiscCount: {
            uint16 discCount;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                discCount = discCount * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumDiscCount = discCount;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemArtistPersistentIdentifier: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->artistID = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemArtist: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->artist = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumArtistPersistentIdentifier: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->albumArtistID = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemAlbumArtist: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->albumArtist = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemGenrePersistentIdentifier: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->genreID = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemGenre: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->genre = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemComposerPersistentIdentifier: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->composerID = id;
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemComposer: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                item->composer = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsPartOfCompilation: {
            item->partOfCompilation = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsLikeSupported: {
            item->likeSupported = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsBanSupported: {
            item->banSupported = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsLiked: {
            item->liked = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsBanned: {
            item->banned = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemIsResidentOnDevice: {
            item->residentOnDevice = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemArtworkFileTransferIdentifier: {
            break;
        }
        case IAP2_MLA_MLU_MI_MediaItemChapterCount: {
            uint32 chapterCount = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                chapterCount = chapterCount * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            item->chapterCount = chapterCount;
            break;
        }
        }
        parsedLen += subParamLen;
    }

    return 0;
}

int CIpodIap2::parseMediaLibraryPlaylist(const uint8 *data, uint16 len, IAP2MediaPlaylist *playlist) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_MLA_MLU_MP_MediaPlaylistPersistentIdentifer: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            playlist->id = id;
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistName: {
            if (subParamLen > IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH) {
                char buf[subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
                memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                playlist->name = buf;
            }
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistParentPersistentIdentifer: {
            uint64 id = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            playlist->parentID = id;
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistIsGeniusMix: {
            playlist->geniusMix = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistIsFolder: {
            playlist->folder = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistContainedMediaItemsFileTransferIdentifier: {
            playlist->fileTransferID = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_MLA_MLU_MP_MediaPlaylistIsAppleMusicRadioStation: {
            playlist->iTunesRadioStation = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_MLA_MLU_MP_MediaPlaylistContentTransfer:
            break;
        }
        }
        parsedLen += subParamLen;
    }

    return 0;
}

int CIpodIap2::startNowPlayingUpdates() {
    CIpodLog::w("Accessory StartNowPlayingUpdates\n");

    uint16 point = 0;
    uint16 subpoint = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint8 data[1024] = { 0 };
    uint8 subdata[1024] = { 0 };
    uint8 tempdata[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    /* MediaItemAttributes start*/
    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemPersistentIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemTitle, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemPlaybackDurationInMilliseconds, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemAlbumTitle, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemAlbumTrackNumber, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemAlbumTrackCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemAlbumDiscNumber, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemAlbumDiscCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemArtist, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemGenre, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemComposer, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemIsLikeSupported, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemIsBanSupported, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemIsLiked, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemIsBanned, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemArtworkFileTransferIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MIA_MediaItemChapterCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_MediaItemAttributes, tempdata, subpoint, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* MediaItemAttributes end*/

    /* PlaybackAttributes start*/
    subpoint = 0;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackStatus, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackElapsedTimeInMilliseconds, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackQueueIndex, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackQueueCount, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackQueueChapterIndex, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackShuffleMode, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackRepeatMode, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackAppName, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackMediaLibraryUniqueIdentifier, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PBAppleMusicRadioAd, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PBAppleMusicRadioStationName, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PBAppleMusicRadioStationMediaPlaylistID, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackSpeed, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_SetElapsedTimeAvailable, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

#if 1
    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackQueueListAvail, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackQueueListTransferID, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;
#endif

    paramLen = createParamPacket(IAP2_NP_SNPU_PA_PlaybackAppBundleID, NULL, 0, subdata);
    memcpy(tempdata + subpoint, subdata, paramLen);
    subpoint += paramLen;

    paramLen = createParamPacket(IAP2_NP_SNPU_PlaybackAttributes, tempdata, subpoint, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
    /* PlaybackAttributes end*/

    packetSize = createPayloadPacket(IAP2_NOW_PLAYING, IAP2_NP_StartNowPlayingUpdates, paramPacket, point, (uint8 *) payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::nowPlayingUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    uint64 lastID = m_NowPlaying.id;
    uint32 lastIndex = m_NowPlaying.index;
    std::string lastTitle = m_NowPlaying.title;
    std::string lastAstist = m_NowPlaying.artist;
    std::string lastAlbum = m_NowPlaying.album;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_NP_NPU_MediaItemAttributes: {
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            parseNowPlayingMediaItem(paramBuf, paramLen);
            free(paramBuf);
            break;
        }
        case IAP2_NP_NPU_PlaybackAttributes: {
            uint8 *paramBuf = (uint8 *)malloc(paramLen);
            memcpy(paramBuf, data + parsedLen, paramLen);
            parseNowPlayingPlayback(paramBuf, paramLen);
            free(paramBuf);
            break;
        }
        }
        parsedLen += paramLen;
    }

    if (lastID != m_NowPlaying.id
            || lastTitle.compare(m_NowPlaying.title) != 0
            || lastAstist.compare(m_NowPlaying.artist) != 0
            || lastAlbum.compare(m_NowPlaying.album) != 0) {
        CIpodEvent::sendEvent(IPOD_EVT_TRACK_CHANGED, getHandle(), m_NowPlaying.index);
    }

    m_NowPlaying.inited = true;
    return 0;
}

int CIpodIap2::stopNowPlayingUpdates() {
    CIpodLog::i("CIpodIap2::stopNowPlayingUpdates\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_NOW_PLAYING, IAP2_NP_StopNowPlayingUpdates, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::setElapsedTime(uint32 time) {
    CIpodLog::i("CIpodIap2::setElapsedTime, time = %lu\n", time);

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    if (time < 0 || !m_NowPlaying.setPositionAvailable) {
        CIpodLog::e("CIpodIap2::setElapsedTime, time is %lu, setPositionAvailable = %d\n", time, m_NowPlaying.setPositionAvailable);
        return -1;
    }

    uint8 timeArray[4] = { 0 };
    time = time * 1000;
    timeArray[0] = (time >> 24) & 0xFF;
    timeArray[1] = (time >> 16)& 0xFF;
    timeArray[2] = (time >> 8) & 0xFF;
    timeArray[3] = (time) & 0xFF;

    packetSize = createParamPacket(IAP2_NP_SNPI_ElapsedTime, (uint8 *) timeArray, 4, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_NOW_PLAYING, IAP2_NP_SetNowPlayingInformation, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::setPlaybackQueueIndex(uint32 index) {
    CIpodLog::i("CIpodIap2::setPlaybackQueueIndex, index = %lu\n", index);

    uint16 packetSize = 0;
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint8 indexArray[4] = { 0 };
    indexArray[0] = (index >> 24) & 0xFF;
    indexArray[1] = (index >> 16)& 0xFF;
    indexArray[2] = (index >> 8) & 0xFF;
    indexArray[3] = (index) & 0xFF;

    packetSize = createParamPacket(IAP2_NP_SNPI_PlaybackQueueIndex, (uint8 *) indexArray, 4, (uint8 *)paramPacket);
    packetSize = createPayloadPacket(IAP2_NOW_PLAYING, IAP2_NP_SetNowPlayingInformation, paramPacket, packetSize, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::parseNowPlayingMediaItem(const uint8 *data, uint16 len) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_NP_NPU_MIA_MediaItemPersistentIdentifier: {
            uint64 tempID = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempID = tempID * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.id = tempID;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemTitle: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.title = buf;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemPlaybackDurationInMilliseconds: {
            uint32 tempDuration = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempDuration = tempDuration * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.duration = (tempDuration + 500) / 1000;
            CIpodEvent::sendEvent(IPOD_EVT_TRACK_DURATION_CHANGED, getHandle(), m_NowPlaying.duration);
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemAlbumTitle: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.album = buf;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemAlbumTrackNumber:
            break;
        case IAP2_NP_NPU_MIA_MediaItemAlbumTrackCount:
            break;
        case IAP2_NP_NPU_MIA_MediaItemAlbumDiscNumber:
            break;
        case IAP2_NP_NPU_MIA_MediaItemAlbumDiscCount:
            break;
        case IAP2_NP_NPU_MIA_MediaItemArtist: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.artist = buf;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemGenre: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.genre = buf;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemComposer: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.composer = buf;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemIsLikeSupported:
            break;
        case IAP2_NP_NPU_MIA_MediaItemIsBanSupported:
            break;
        case IAP2_NP_NPU_MIA_MediaItemIsLiked:
            break;
        case IAP2_NP_NPU_MIA_MediaItemIsBanned:
            break;
        case IAP2_NP_NPU_MIA_MediaItemArtworkFileTransferIdentifier: {
            IAP2FileTransfer *transfer = (IAP2FileTransfer *) malloc(sizeof(IAP2FileTransfer));
            memset(transfer, 0, sizeof(IAP2FileTransfer));
            transfer->id = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            transfer->type = IAP2FileTransferType::ARTWORK;
            m_FileTransfers.push_back(transfer);
            m_NowPlaying.artworkInited = false;
            break;
        }
        case IAP2_NP_NPU_MIA_MediaItemChapterCount: {
            uint32 tempChapterCount = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempChapterCount = tempChapterCount * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.chapterCount = tempChapterCount;
            break;
        }
        }
        parsedLen += subParamLen;
    }

    return 0;
}

int CIpodIap2::parseNowPlayingPlayback(const uint8 *data, uint16 len) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_NP_NPU_PA_PlaybackStatus: {
            m_NowPlaying.state = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            CIpodEvent::sendEvent(IPOD_EVT_PLAYBACK_STATE_CHANGED, getHandle(), m_NowPlaying.state);
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackElapsedTimeInMilliseconds: {
            uint32 tempPosition = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempPosition = tempPosition * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }

            tempPosition = (tempPosition + 500) / 1000;
            if (tempPosition != m_NowPlaying.position) {
                m_NowPlaying.position = tempPosition;
                CIpodEvent::sendEvent(IPOD_EVT_TRACK_POSITION_CHANGED, getHandle(), m_NowPlaying.position);
            }
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackQueueIndex: {
            uint32 tempTrackIndex = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempTrackIndex = tempTrackIndex * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.index = tempTrackIndex;
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackQueueCount: {
            uint32 tempTrackCount = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempTrackCount = tempTrackCount * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.count = tempTrackCount;
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackQueueChapterIndex: {
            uint32 tempChapterIndex = 0;
            for (int32 i = 0; i < subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                tempChapterIndex = tempChapterIndex * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_NowPlaying.chapterIndex = tempChapterIndex;
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackShuffleMode: {
            m_NowPlaying.shuffleMode = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            CIpodEvent::sendEvent(IPOD_EVT_SHUFFLE_MODE_CHANGED, getHandle(), m_NowPlaying.shuffleMode);
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackRepeatMode: {
            m_NowPlaying.repeatMode = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            CIpodEvent::sendEvent(IPOD_EVT_REPEAT_MODE_CHANGED, getHandle(), m_NowPlaying.repeatMode);
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackAppName: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.appName = buf;
            break;
        }
        case IAP2_NP_NPU_PA_PBMediaLibraryUniqueIdentifier: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.libraryID = buf;

            char cachePath[128];
            sprintf(cachePath, "%s/%s", CIpodVehicle::getCachePath(), buf);
            if (access(cachePath, 0) != 0) {
                mkdir(cachePath, 0777);
                CIpodEvent::sendEvent(IPOD_EVT_FILE_MUSIC_RADIO_PATH, getHandle(), cachePath);
            }
            break;
        }
        case IAP2_NP_NPU_PA_PBAppleMusicRadioAd:
            break;
        case IAP2_NP_NPU_PA_PBAppleMusicRadioStationName:
            break;
        case IAP2_NP_NPU_PA_PBAppleMusicRadioStationMediaPlaylistID:
            break;
        case IAP2_NP_NPU_PA_PlaybackSpeed:
            break;
        case IAP2_NP_NPU_PA_SetElapsedTimeAvailable: {
            m_NowPlaying.setPositionAvailable = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackQueueListAvail: {
            m_NowPlaying.queueListAvailable = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            CIpodEvent::sendEvent(IPOD_EVT_PLAYBACK_QUEUELIST_AVAILABLE, getHandle(), m_NowPlaying.queueListAvailable);
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackQueueListTransferID: {
            IAP2FileTransfer *transfer = (IAP2FileTransfer *) malloc(sizeof(IAP2FileTransfer));
            memset(transfer, 0, sizeof(IAP2FileTransfer));
            transfer->id = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            transfer->type = IAP2FileTransferType::PLAYING_LIST;
            m_FileTransfers.push_back(transfer);
            break;
        }
        case IAP2_NP_NPU_PA_PlaybackAppBundleID: {
            char buf[subParamLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
            m_NowPlaying.appBundleID = buf;
            break;
        }
        }
        parsedLen += subParamLen;
    }
    return 0;
}

int CIpodIap2::startOOBBTPairing(const uint8 *data) {
    CIpodLog::w("[  Device   ] StartOOBBTPairing.\n");

    oOBBTPairingAccessoryInformation();
    return 0;
}
#define	WriteBig16( PTR, X ) \
	do \
	{ \
		( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >>  8 ) & 0xFF ); \
		( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)(   (X)         & 0xFF ); \
	\
	}	while( 0 )

#define	WriteBig32( PTR, X ) \
    do \
    { \
        ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >>  8 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)(   (X)         & 0xFF ); \
    \
    }	while( 0 )

#define	WriteLittle32( PTR, X ) \
    do \
    { \
        ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)(   (X)         & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >>  8 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \
    \
    }	while( 0 )

int CIpodIap2::oOBBTPairingAccessoryInformation() {
    CIpodLog::w("[ Accessory ] OOBBTPairingAccessoryInformation.\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 bluetoothTransport_id = CIpodConfig::getIdentification()->bluetoothTransport.id;
    uint16 identifier;
    WriteBig16( (uint8 *)&identifier, bluetoothTransport_id );
    paramLen = createParamPacket(IAP2_R2_OOBBTP_AI_BluetoothTransportComponentIdentifier,(uint8 *) &identifier, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    uint8_t deviceClass[4];
    WriteBig32( deviceClass, 0x240408 );
    paramLen = createParamPacket(IAP2_R2_OOBBTP_AI_DeviceClass,(uint8 *) &deviceClass, 4, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_R2_OOBBT_PAIRING, IAP2_R2_OOBBTP_OOBBTPairingAccessoryInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::oOBBTPairingLinkKeyInformation(const uint8 *data) {
    CIpodLog::w("[  Device   ] OOBBTPairingLinkKeyInformation.\n");

    OOBLinkKeyInformation linkKey;

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_R2_OOBBTP_LI_LinkKey: {
            memcpy(linkKey.link_key, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH );
            linkKey.link_key_len = paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
            break;
        }
        case IAP2_R2_OOBBTP_LI_DeviceMACAddress:
        {
            char buf[paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            char addrlog[32];
            memset( linkKey.mac_address, 0, 32 );
            memset( addrlog, 0, 32 );
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            sprintf(addrlog, "%2x:%2x:%2x:%2x:%2x:%2x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);

            memcpy(linkKey.mac_address, addrlog, 32);
            break;
        }
        }
        parsedLen += paramLen;
    }

    CIpodEvent::sendEvent(IPOD_EVT_DEVICE_LINK_KEY_INFORMATION, getHandle(), &linkKey);
    return 0;
}

int CIpodIap2::oOBBTPairingCompletionInformation(int resultCode) {
    CIpodLog::w("[ Accessory ] OOBBTPairingCompletionInformation.\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint8_t ResultCode = (resultCode == 1)? 0:1;
    paramLen = createParamPacket(IAP2_R2_OOBBTP_CI_ResultCode,(uint8 *) &ResultCode, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_R2_OOBBT_PAIRING, IAP2_R2_OOBBTP_OOBBTPairingCompletionInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::stopOOBBTPairing(const uint8 *data) {
    CIpodLog::w("[  Device   ] StopOOBBTPairing.\n");
    return 0;
}

int CIpodIap2::startBluetoothPairing(const uint8 *data) {
    CIpodLog::i("CIpodIap2::startBluetoothPairing \n");

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_OOBBTP_StrBP_BluetoothTransportComponentIdentifier: {
            break;
        }
        case IAP2_OOBBTP_StrBP_DeviceMACAddress: {
            char buf[paramLen];
            memcpy(buf, data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            char addrlog[128];
            sprintf(addrlog, "%2x:%2x:%2x:%2x:%2x:%2x",
                    buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
            CIpodLog::i("startBluetoothPairing IAP2_OOBBTP_StrBP_DeviceMACAddress is %s!\n", addrlog);
            CIpodEvent::sendEvent(IPOD_EVT_DEVICE_MAC_ADDRESS, getHandle(), addrlog);
            break;
        }
        }
        parsedLen += paramLen;
    }

    return 0;
}

int CIpodIap2::bluetoothPairingAccessoryInformation(const char * inPairingDataP192,const char * inPairingDataP256) {
    CIpodLog::i("CIpodIap2::bluetoothPairingAccessoryInformation\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 bluetoothTransport_id = CIpodConfig::getIdentification()->bluetoothTransport.id;
    paramLen = createParamPacket(IAP2_OOBBTP_BPAI_BluetoothTransportComponentIdentifier,(uint8 *) &bluetoothTransport_id, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // PairingDataP192
    uint8 PairingDataP192[ 2 ][ 16 ];
    memset( PairingDataP192, 0, 32 );
    memcpy( PairingDataP192, inPairingDataP192, 32 );
    paramLen = createParamPacket(IAP2_OOBBTP_BPAI_PairingDataP192,(uint8 *) &PairingDataP192, 32, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    // PairingDataP256
    uint8_t PairingDataP256[ 2 ][ 16 ];
    memset( PairingDataP256, 0, 32 );
    memcpy( PairingDataP256, inPairingDataP256, 32 );
    paramLen = createParamPacket(IAP2_OOBBTP_BPAI_PairingDataP256,(uint8 *) &PairingDataP256, 32, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_OOBBT_PAIRING, IAP2_OOBBTP_BluetoothPairingAccessoryInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::bluetoothPairingStatus(int success, const char * cancelReason) {
    CIpodLog::i("CIpodIap2::bluetoothPairingStatus\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 bluetoothTransport_id = CIpodConfig::getIdentification()->bluetoothTransport.id;
    paramLen = createParamPacket(IAP2_OOBBTP_BPS_BluetoothTransportComponentIdentifier,(uint8 *) &bluetoothTransport_id, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    if(success){
        paramLen = createParamPacket(IAP2_OOBBTP_BPS_Success,NULL, 0, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    } else {
        paramLen = createParamPacket(IAP2_OOBBTP_BPS_CancelReason,(uint8 *) &cancelReason, strlen( cancelReason ), data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }
    packetSize = createPayloadPacket(IAP2_OOBBT_PAIRING, IAP2_OOBBTP_BluetoothPairingStatus, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::stopBluetoothPairing(const uint8 *data) {
    CIpodLog::i("CIpodIap2::stopBluetoothPairing \n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_OOBBTP_SBP_BluetoothTransportComponentIdentifier: {
            break;
        }
        case IAP2_OOBBTP_SBP_Success: {
            break;
        }
        case IAP2_OOBBTP_SBP_AlreadyPaired: {
            break;
        }
        case IAP2_OOBBTP_SBP_PairingCancel: {
            break;
        }
        }
        parsedLen += paramLen;
    }

    return 0;
}

int CIpodIap2::startPowerUpdates() {
    CIpodLog::i("[ Accessory ] StartPowerUpdates.\n");

    uint16 point = 0;
    uint16 paramLen = 0;
    uint16 packetSize = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    paramLen = createParamPacket(IAP2_P_SPU_MaximumCurrentDrawnFromAccessory, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_P_SPU_DeviceBatteryWillChargeIfPowerIsPresent, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

#if 0
    paramLen = createParamPacket(IAP2_P_SPU_AccessoryPowerMode, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;
#endif

    paramLen = createParamPacket(IAP2_P_SPU_IsExternalChargerConnected, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_P_SPU_BatteryChargingState, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_P_SPU_BatteryChargeLevel, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_POWER, IAP2_P_StartPowerUpdates, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::powerUpdate(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_P_PU_MaximumCurrentDrawnFromAccessory: {
            uint16 maxDrawn = IAP2_BIG_ENDIAN_VALUE(parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            CIpodLog::i("[  Device   ] PowerUpdate MaximumCurrentDrawnFromAccessory: %d\n", maxDrawn);
            break;
        }
        case IAP2_P_PU_DeviceBatteryWillChargeIfPowerIsPresent: {
            bool willCharge = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            CIpodLog::i("[  Device   ] PowerUpdate DeviceBatteryWillChargeIfPowerIsPresent: %d\n", willCharge);
            break;
        }
        case IAP2_P_PU_AccessoryPowerMode: {
            int powerMode = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            CIpodLog::i("CIpodIap2::powerUpdate, powerMode = %d\n", powerMode);
            break;
        }
        case IAP2_P_PU_IsExternalChargerConnected: {
            bool chargerConnected = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            CIpodLog::i("[  Device   ] PowerUpdate IsExternalChargerConnected: %d\n", chargerConnected);
            break;
        }
        case IAP2_P_PU_BatteryChargingState: {
            int chargingState = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            CIpodLog::i("[  Device   ] PowerUpdate BatteryChargingState: %d\n", chargingState);
            break;
        }
        case IAP2_P_PU_BatteryChargeLevel: {
            uint16 chargeLevel = IAP2_BIG_ENDIAN_VALUE(parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            CIpodLog::i("[  Device   ] PowerUpdate BatteryChargeLevel: %d\n", chargeLevel);
            break;
        }
        }
        parsedLen += paramLen;
    }

    return 0;
}

int CIpodIap2::stopPowerUpdates() {
    CIpodLog::i("CIpodIap2::stopPowerUpdates\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_POWER, IAP2_P_StopPowerUpdates, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::powerSourceUpdate(uint16 value) {
    CIpodLog::i("[ Accessory ] PowerSourceUpdate.\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 powerValue = IAP2_BIG_ENDIAN_SWITCH(value);
    paramLen = createParamPacket(IAP2_P_PSU_AvailableCurrentForDevice, (uint8 *)&powerValue, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    bool charge = true;
    paramLen = createParamPacket(IAP2_P_PSU_DeviceBatteryShouldChargeIfPowerIsPresent, (uint8 *)&charge, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_POWER, IAP2_P_PowerSourceUpdate, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

#define	WriteLittle64( PTR, X ) \
    do \
    { \
        ( (uint8_t *)(PTR) )[ 0 ] = (uint8_t)(   (X)         & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 1 ] = (uint8_t)( ( (X) >>  8 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 2 ] = (uint8_t)( ( (X) >> 16 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 3 ] = (uint8_t)( ( (X) >> 24 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 4 ] = (uint8_t)( ( (X) >> 32 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 5 ] = (uint8_t)( ( (X) >> 40 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 6 ] = (uint8_t)( ( (X) >> 48 ) & 0xFF ); \
        ( (uint8_t *)(PTR) )[ 7 ] = (uint8_t)( ( (X) >> 56 ) & 0xFF ); \
    \
    }	while( 0 )

#define	ReadBig64( PTR ) \
	( (uint64_t)( \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 0 ] ) << 56 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 1 ] ) << 48 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 2 ] ) << 40 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 3 ] ) << 32 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 4 ] ) << 24 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 5 ] ) << 16 ) | \
		( ( (uint64_t)( (uint8_t *)(PTR) )[ 6 ] ) <<  8 ) | \
		  ( (uint64_t)( (uint8_t *)(PTR) )[ 7 ] ) ) )

int CIpodIap2::startRouteGuidanceUpdate() {
    CIpodLog::w("Accessory StartRouteGuidanceUpdates.\n");

    memset( m_route_guidance_update.distanceRemainingDisplayStr, '\0', 128 );
    m_route_guidance_update.routeGuidanceState = 0;

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 routeGuidance_id;

    WriteBig16( (uint8 *)&routeGuidance_id, CIpodConfig::getIdentification()->routeGuidance.id );
    paramLen = createParamPacket(IAP2_RG_StrRGU_RouteGuidanceDisplayComponentID,(uint8 *) &routeGuidance_id, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_RG_StrRGU_SourceName, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_RG_StrRGU_SourceSupportsRouteGuidance, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    paramLen = createParamPacket(IAP2_RG_StrRGU_SupportsExitInfo, NULL, 0, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_ROUTE_GUIDANCE, IAP2_RG_StartRouteGuidanceUpdates, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::routeGuidanceUpdate(const uint8 *data) {
    // CIpodLog::i("CIpodIap2::routeGuidanceUpdate\n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_RG_RGU_RouteGuidanceDisplayComponentID:
            m_route_guidance_update.routeGuidanceDisplayComponentID = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_RouteGuidanceState:
            m_route_guidance_update.routeGuidanceState = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_ManeuverState:
            m_route_guidance_update.maneuverState = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_CurrentRoadName:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_update.currentRoadName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_update.currentRoadName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_update.currentRoadName[127]='\0';
            }
            break;
        case IAP2_RG_RGU_DestinationName:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_update.destinationName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_update.destinationName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_update.destinationName[127]='\0';
            }
            break;
        case IAP2_RG_RGU_EstimatedTimeOfArrival:
        {
            // uint64 id = 0;
            // for (int32 i = 0; i < parsedLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
            //     id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            // }
            // m_route_guidance_update.estimatedTimeOfArrival = id;
            break;
        }
        case IAP2_RG_RGU_TimeRemainingToDestination:
        {
            uint64 id = 0;
            uint64 temp = 0;

            temp = ReadBig64( &data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH] );

            WriteLittle64( (uint8_t *)&id, temp );
            m_route_guidance_update.timeRemainingToDestination = id;

            // CIpodLog::i( "Device RouteGuidanceUpdate TimeRemainingToDestination : %d.\n", id );
            break;
        }
        case IAP2_RG_RGU_DestinationRemaining:
        {
            // int32 id = 0;
            // for (int32 i = 0; i < parsedLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
            //     id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            // }
            // m_route_guidance_update.distanceRemaining = id;
            break;
        }
        case IAP2_RG_RGU_DestinationRemainingDisplayStr:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_update.distanceRemainingDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_update.distanceRemainingDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_update.distanceRemainingDisplayStr[127]='\0';
            }
            break;
        case IAP2_RG_RGU_DestinationRemainingDisplayUnits:
            m_route_guidance_update.distanceRemainingDisplayUnits = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_DistanceToNextManeuver:
        {
            int32 id = 0;
            for (int32 i = 0; i < parsedLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_route_guidance_update.distanceToNextManeuver = id;
            break;
        }
        case IAP2_RG_RGU_DistanceToNextManeuverDisplayStr:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_update.distanceToNextManeuverDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_update.distanceToNextManeuverDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_update.distanceToNextManeuverDisplayStr[127]='\0';
            }
            break;
        case IAP2_RG_RGU_DistanceToNextManeuverDisplayUnits:
            m_route_guidance_update.distanceToNextManeuverDisplayUnits = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_RouteGuidanceManeuverCurrentList:
            break;
        case IAP2_RG_RGU_RouteGuidanceManeuverCount:
            m_route_guidance_update.routeGuidanceManeuverCount = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGU_RouteGuidanceVisibleInApp:
            m_route_guidance_update.routeGuidanceVisibleInApp = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += paramLen;
    }

    if( m_route_guidance_update.routeGuidanceState == ROUTE_GUIDANCE_STATE_NO_ROUTE_SET )
    {
        CIpodLog::i("[  Device   ] RouteGuidanceUpdate No Route Set. A route is not set.\n");
        return 0;
    }

    CIpodEvent::sendEvent(IPOD_EVT_ROUTE_GUIDANCE_CHANGED, getHandle(),&m_route_guidance_update );

    return 0;
}

int CIpodIap2::routeGuidanceManeuverInformation(const uint8 *data) {
    // CIpodLog::i("CIpodIap2::routeGuidanceManeuverInformation \n");
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_RG_RGMU_RouteGuidanceDisplayComponentID:
            m_route_guidance_maneuver_update.routeGuidanceDisplayComponentID = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_Index:
            m_route_guidance_maneuver_update.index = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_ManeuverDescription:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_maneuver_update.maneuverDescription,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_maneuver_update.maneuverDescription,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_maneuver_update.maneuverDescription[127]='\0';
            }
            break;
        case IAP2_RG_RGMU_ManeuverType:
            m_route_guidance_maneuver_update.maneuverType = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_AfterManeuverRoadName:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_maneuver_update.afterManeuverRoadName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_maneuver_update.afterManeuverRoadName,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_maneuver_update.afterManeuverRoadName[127]='\0';
            }
            break;
        case IAP2_RG_RGMU_DistanceBetweenManeuver:
        {
            int32 id = 0;
            for (int32 i = 0; i < parsedLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH; i++) {
                id = id * 256 + data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH + i];
            }
            m_route_guidance_maneuver_update.distanceBetweenManeuver = id;
            break;
        }
        case IAP2_RG_RGMU_DistanceBetweenManeuverDisplayStr:
            if(paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 128)
            {
                memcpy(m_route_guidance_maneuver_update.distanceBetweenManeuverDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, paramLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH);
            }
            else
            {
                memcpy(m_route_guidance_maneuver_update.distanceBetweenManeuverDisplayStr,data + parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH, 127);
                m_route_guidance_maneuver_update.distanceBetweenManeuverDisplayStr[127]='\0';
            }
            break;
        case IAP2_RG_RGMU_DistanceBetweenManeuverDisplayUnits:
            m_route_guidance_maneuver_update.distanceBetweenManeuverDisplayUnits = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_DrivingSide:
            m_route_guidance_maneuver_update.drivingSide = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_JunctionType:
            m_route_guidance_maneuver_update.junctionType = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_JunctionElementAngle:
            m_route_guidance_maneuver_update.junctionElementAngle = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_JunctionElementExitAngle:
            m_route_guidance_maneuver_update.junctionElementExitAngle = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        case IAP2_RG_RGMU_LinkedLaneGuidanceInformation:
            break;
        case IAP2_RG_RGMU_ExitInfo:
            break;
        }
        parsedLen += paramLen;
    }

    CIpodEvent::sendEvent(IPOD_EVT_ROUTE_GUIDANCE_MANEUVER_CHANGED, getHandle(),&m_route_guidance_maneuver_update );

    return 0;
}

int CIpodIap2::stopRouteGuidanceUpdate() {
    CIpodLog::i("CIpodIap2::stopRouteGuidanceUpdate\n");

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    uint16 routeGuidance_id = CIpodConfig::getIdentification()->routeGuidance.id;
    paramLen = createParamPacket(IAP2_RG_StpRGU_RouteGuidanceDisplayComponentID,(uint8 *) &routeGuidance_id, 2, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_ROUTE_GUIDANCE, IAP2_RG_StopRouteGuidanceUpdates, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::startUSBDeviceModeAudio() {
    CIpodLog::i("CIpodIap2::startUSBDeviceModeAudio\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_USB_DEVICE_MODE_AUDIO, IAP2_USBDMA_StartUSBDeviceModeAudio, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::usbDeviceModeAudioInformatin(const uint8 *data) {
    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;
    uint8 sampleRateID;
    uint32 sampleRate;
    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID) {
        case IAP2_USBDMA_USBDMAI_SampleRate:
            sampleRateID = data[parsedLen + IAP2_MESSAGE_PARAMETER_HEADER_LENGTH];
            break;
        }
        parsedLen += paramLen;
    }
    switch(sampleRateID) {
    case 0:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_8000;
        break;
    case 1:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_11025;
        break;
    case 2:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_12000;
        break;
    case 3:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_16000;
        break;
    case 4:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_22050;
        break;
    case 5:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_24000;
        break;
    case 6:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_32000;
        break;
    case 7:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_44100;
        break;
    case 8:
        sampleRate = USB_DEVICE_AUDIO_SAMPLE_RATE_48000;
        break;
    }

    CIpodLog::i("CIpodIap2::usbDeviceModeAudioInformation, sampleRate = %d\n", sampleRate);
    CIpodEvent::sendEvent(IPOD_EVT_SAMPLE_RATE_CHANGED, getHandle(), sampleRate);
    return 0;
}

int CIpodIap2::stopUSBDeviceModeAudio() {
    CIpodLog::i("CIpodIap2::stopUSBDeviceModeAudio\n");

    uint16 packetSize = 0;
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };

    packetSize = createPayloadPacket(IAP2_USB_DEVICE_MODE_AUDIO, IAP2_USBDMA_StopUSBDeviceModeAudio, NULL, 0, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *)&linkPacketOut, packetSize);
}

int CIpodIap2::startVehicleStatusUpdates(const uint8 *data) {
    CIpodLog::i("CIpodIap2::startVehicleStatusUpdates, not support!\n");
    return -1;
}

int CIpodIap2::vehicleStatusUpdate() {
    CIpodLog::i("CIpodIap2::vehicleStatusUpdate, not support!\n");
    return -1;
}

int CIpodIap2::stopVehicleStatusUpdates() {
    CIpodLog::i("CIpodIap2::stopVehicleStatusUpdates, not support!\n");
    return -1;
}

int CIpodIap2::startVoiceOver() {
    CIpodLog::i("CIpodIap2::startVoiceOver, not support!\n");
    return -1;
}

int CIpodIap2::stopVoiceOver() {
    CIpodLog::i("CIpodIap2::stopVoiceOver, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverMoveCursor() {
    CIpodLog::i("CIpodIap2::requestVoiceOverMoveCursor, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverActivateCursor() {
    CIpodLog::i("CIpodIap2::requestVoiceOverActivateCursor, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverScrollPage() {
    CIpodLog::i("CIpodIap2::requestVoiceOverScrollPage, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverSpeakText() {
    CIpodLog::i("CIpodIap2::requestVoiceOverSpeakText, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverPauseText() {
    CIpodLog::i("CIpodIap2::requestVoiceOverPauseText, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverResumeText() {
    CIpodLog::i("CIpodIap2::requestVoiceOverResumeText, not support!\n");
    return -1;
}

int CIpodIap2::startVoiceOverUpdates() {
    CIpodLog::i("CIpodIap2::startVoiceOverUpdates, not support!\n");
    return -1;
}

int CIpodIap2::voiceOverUpdate(const uint8 *data) {
    CIpodLog::i("CIpodIap2::voiceOverUpdate, not support!\n");
    return -1;
}

int CIpodIap2::stopVoiceOverUpdates() {
    CIpodLog::i("CIpodIap2::stopVoiceOverUpdates, not support!\n");
    return -1;
}

int CIpodIap2::requestVoiceOverConfiguration() {
    CIpodLog::i("CIpodIap2::requestVoiceOverConfiguration, not support!\n");
    return -1;
}

int CIpodIap2::startVoiceOverCursorUpdates() {
    CIpodLog::i("CIpodIap2::startVoiceOverCursorUpdates, not support!\n");
    return -1;
}

int CIpodIap2::voiceOverCursorUpdate(const uint8 *data) {
    CIpodLog::i("CIpodIap2::voiceOverCursorUpdate, not support!\n");
    return -1;
}

int CIpodIap2::stopVoiceOverCursorUpdates() {
    CIpodLog::i("CIpodIap2::stopVoiceOverCursorUpdates, not support!\n");
    return -1;
}

int CIpodIap2::requestWiFiInformation() {
    CIpodLog::i("CIpodIap2::requestWiFiInformation, not support!\n");
    return -1;
}

int CIpodIap2::wifiInformation(const uint8 *data) {
    CIpodLog::i("CIpodIap2::wifiInformation, not support!\n");
    return -1;
}

int CIpodIap2::requestAccessoryWiFiConfigurationInformation() {
    CIpodLog::i("[  Device   ] RequestAccessoryWiFiConfigurationInformation\n");
    return accessoryWiFiConfigurationInformation();
}

int CIpodIap2::accessoryWiFiConfigurationInformation()
{
    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    char tempStr[1024] = {0};

    //WiFiSSID
    memset( tempStr, '\0', sizeof( tempStr ) );
    strncpy( tempStr, CIpodVehicle::getWifiInformarion()->wifi_ssid, strlen( CIpodVehicle::getWifiInformarion()->wifi_ssid ) );
    paramLen = createParamPacket(IAP2_WIFIIS_AWIFICI_WiFiSSID,(uint8 *) &tempStr, strlen(tempStr) + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //Passphrase
    memset( tempStr, '\0', sizeof( tempStr ) );
    strncpy( tempStr, CIpodVehicle::getWifiInformarion()->pass_phrase, strlen( CIpodVehicle::getWifiInformarion()->pass_phrase ) );
    paramLen = createParamPacket(IAP2_WIFIIS_AWIFICI_Passphrase,(uint8 *) &tempStr, strlen(tempStr) + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //SecurityType
    uint8 securityType = CIpodVehicle::getWifiInformarion()->security_type;
    paramLen = createParamPacket(IAP2_WIFIIS_AWIFICI_SecurityType,(uint8 *) &securityType, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //Channel
    uint8 channel = CIpodVehicle::getWifiInformarion()->channel;
    paramLen = createParamPacket(IAP2_WIFIIS_AWIFICI_Channel,(uint8 *) &channel, 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    CIpodLog::i("[ Accessory ] AccessoryWiFiConfigurationInformation WiFiSSID: %s, Passphrase: %s, SecurityType: %d, Channel: %d.\n",
        CIpodVehicle::getWifiInformarion()->wifi_ssid,
        CIpodVehicle::getWifiInformarion()->pass_phrase,
        securityType, channel );

    packetSize = createPayloadPacket(IAP2_WIFI_INFORMATION_SHARING, IAP2_WIFIIS_AccessoryWiFiConfigurationInformation, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

void CIpodIap2::getIPv6( char * address, const char * inInterfaceName )
{
    struct ifaddrs *    ifaList;
    struct ifaddrs *    ifa;
    void *              tmpAddress = NULL;
    char                addressBuffer[INET6_ADDRSTRLEN];
    int                 length;

    getifaddrs(&ifaList);

    for (ifa = ifaList; ifa; ifa = ifa->ifa_next)
    {
        if (!strcmp( ifa->ifa_name, inInterfaceName ) && ifa->ifa_addr->sa_family == AF_INET6)
        {
            tmpAddress = &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr;
            inet_ntop(AF_INET6, tmpAddress, addressBuffer, INET6_ADDRSTRLEN);

        }
    }

    if (ifaList)    freeifaddrs(ifaList);
    length = strlen(addressBuffer);
    memcpy(address, addressBuffer, length);
    address[length] = '\0';
}

int CIpodIap2::carPlayStartSession(int type){
    CIpodLog::w( "[ Accessory ] CarPlayStartSession Type: %d.\n", type );

    uint16 packetSize = 0;
    uint16 paramLen = 0;
    uint16 point = 0;
    uint8 data[1024] = { 0 };
    uint8 sub_data[1024] = { 0 };
    uint8 tempPacket[1024] = { 0 };
    uint8 paramPacket[IAP2_MAXIMUM_PAYLOAD_LENGTH] = { 0 };
    uint8 payloadPacket[IAP2_MAXIMUM_PACKET_LENGTH_OUT] = { 0 };
    char  tempStr[128] = {0};
    uint16 sub_point = 0;

    //WiredAttributes
    if( type == 0 )
    {
        memset( tempStr, '\0', sizeof( tempStr ) );
        getIPv6(tempStr, "ncm0");
        paramLen = createParamPacket(IAP2_CPC_CPSS_W_IPAddress,(uint8 *) &tempStr, strlen(tempStr) + 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        paramLen = createParamPacket(IAP2_CPC_CPSS_WiredAttributes, tempPacket, sub_point, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    //WirelessAttributes
    if( type == 1 )
    {
        memset(tempStr, '\0', sizeof( tempStr ) );
        strncpy( tempStr, CIpodVehicle::getWifiInformarion()->wifi_ssid, strlen( CIpodVehicle::getWifiInformarion()->wifi_ssid ) );
        paramLen = createParamPacket(IAP2_CPC_CPSS_WL_WiFiSSID,(uint8 *) &tempStr, strlen(tempStr) + 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        memset(tempStr, '\0', sizeof( tempStr ) );
        strncpy( tempStr, CIpodVehicle::getWifiInformarion()->pass_phrase, strlen( CIpodVehicle::getWifiInformarion()->pass_phrase ) );
        paramLen = createParamPacket(IAP2_CPC_CPSS_WL_Passphrase,(uint8 *) &tempStr, strlen(tempStr) + 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        uint8 channel = CIpodVehicle::getWifiInformarion()->channel;
        paramLen = createParamPacket(IAP2_CPC_CPSS_WL_Channel,&channel, 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        memset(tempStr, '\0', sizeof( tempStr ) );
        getIPv6(tempStr, "wlan1");
        paramLen = createParamPacket(IAP2_CPC_CPSS_WL_IPAddress,(uint8 *) &tempStr, strlen(tempStr) + 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        uint8 securityType = 3;
        paramLen = createParamPacket(IAP2_CPC_CPSS_WL_SecurityType,&securityType, 1, sub_data);
        memcpy(tempPacket + sub_point, sub_data, paramLen);
        sub_point += paramLen;

        paramLen = createParamPacket(IAP2_CPC_CPSS_WirelessAttributes, tempPacket, sub_point, data);
        memcpy(paramPacket + point, data, paramLen);
        point += paramLen;
    }

    //Port
    uint32 	port  = 1478164480;
    paramLen = createParamPacket(IAP2_CPC_CPSS_Port,(uint8 *) &port, 4, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //DeviceIdentifier
    memset( tempStr, '\0', sizeof( tempStr ) );
    _getBluetoothAddress( tempStr );
    paramLen = createParamPacket(IAP2_CPC_CPSS_DeviceIdentifier,(uint8 *) &tempStr, strlen(tempStr) + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //PublicKey

    paramLen = createParamPacket(IAP2_CPC_CPSS_PublicKey,(uint8 *) &gPairingIdentity, strlen( gPairingIdentity ) + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    //SourceVersion
    memset( tempStr, '\0', sizeof( tempStr ) );
    char sourceVersion[] = "450.14";
    memcpy( tempStr, sourceVersion, strlen(sourceVersion) +1 );
    paramLen = createParamPacket(IAP2_CPC_CPSS_SourceVersion,(uint8 *) &tempStr, strlen(tempStr) + 1, data);
    memcpy(paramPacket + point, data, paramLen);
    point += paramLen;

    packetSize = createPayloadPacket(IAP2_CARPLAY_CONNECTION, IAP2_CPC_CarPlayStartSession, paramPacket, point, (uint8 *)payloadPacket);

    IAP2_CONTROL_BYTE control;
    control.value = 0x00;
    control.ACK = 1;
    IAP2_LINK_PACKET_OUT linkPacketOut;
    increaseSyncNumber();
    packetSize = createLinkPacket(control, m_SyncNumber, m_AckNumber, IAP2_SESSION_ID_CONTROL, payloadPacket, packetSize, &linkPacketOut);
    return write((uint8 *) &linkPacketOut, packetSize);
}

int CIpodIap2::carPlayAvailability(const uint8 *data) {

    uint16 paramLen = 0;
    uint16 paramID = 0;
    uint16 payloadLen = IAP2_BIG_ENDIAN_VALUE(2);
    uint16 parsedLen = IAP2_CONTROL_SESSION_HEADER_LENGTH;

    m_CarPlayAvail.wireAvailable = 0;
    m_CarPlayAvail.wirelessAvailable = 0;

    while (parsedLen < payloadLen) {
        paramLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        paramID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (paramID)
        {
            case IAP2_CPC_CPA_WiredAttributes:
            {
                uint8 *paramBuf = (uint8 *)malloc(paramLen);
                memcpy(paramBuf, data + parsedLen, paramLen);
                parseCarPlayAvailabilityWiredAttributes(paramBuf, paramLen);
                free(paramBuf);
                break;
            }
            case IAP2_CPC_CPA_WirelessAttributes:
            {
                uint8 *paramBuf = (uint8 *)malloc(paramLen);
                memcpy(paramBuf, data + parsedLen, paramLen);
                parseCarPlayAvailabilityWirelessAttributes(paramBuf, paramLen);
                free(paramBuf);
                break;
            }
        }
        parsedLen += paramLen;
    }

    CIpodEvent::sendEvent(IPOD_EVT_CARPLAY_AVAILABILITY_INFORMATION, getHandle(), &m_CarPlayAvail);


    return 0;
}

int CIpodIap2::parseCarPlayAvailabilityWiredAttributes(const uint8 *data, uint16 len) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_CPC_CPA_W_Available: {
            m_CarPlayAvail.wireAvailable = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_CPC_CPA_W_USBTransportIdentifier: {
            if(m_CarPlayAvail.wireAvailable == 1 && subParamLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 32){
                memcpy(m_CarPlayAvail.USBTransportIdentifier, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                CIpodLog::i("[ Accessory ] WireCarPlayAvailability     - USBTransportIdentifier: %s\n", m_CarPlayAvail.USBTransportIdentifier);
            }
           break;
        }
        }
        parsedLen += subParamLen;
    }
    return 0;
}

int CIpodIap2::parseCarPlayAvailabilityWirelessAttributes(const uint8 *data, uint16 len) {
    uint16 parsedLen = 0;
    uint16 subParamLen = 0;
    uint16 subParamID = 0;

    parsedLen += IAP2_MESSAGE_PARAMETER_HEADER_LENGTH;
    while (parsedLen < len) {
        subParamLen = IAP2_BIG_ENDIAN_VALUE(parsedLen);
        subParamID = IAP2_BIG_ENDIAN_VALUE(parsedLen + 2);
        switch (subParamID) {
        case IAP2_CPC_CPA_WL_Available: {
            m_CarPlayAvail.wirelessAvailable = data[parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH];
            break;
        }
        case IAP2_CPC_CPA_WL_BluetoothTransportIdentifier: {
            if(m_CarPlayAvail.wirelessAvailable == 1 && subParamLen - IAP2_MESSAGE_PARAMETER_HEADER_LENGTH < 32){
                memcpy(m_CarPlayAvail.BluetoothTransportIdentifier, data + parsedLen + IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH, subParamLen - IAP2_MESSAGE_SUB_PARAMETER_HEADER_LENGTH);
                CIpodLog::i("[ Accessory ] WirelessCarPlayAvailability - BluetoothTransportIdentifier: %s\n", m_CarPlayAvail.BluetoothTransportIdentifier);
            }
           break;
        }
        }
        parsedLen += subParamLen;
    }
    return 0;
}
